# Node-RED container with Dashboard 2.0 baked in # Build once; restart containers freely. NR's automatic package.json install # at boot is slow and requires internet; this Dockerfile install is cached # and reproducible. # # Docker volume pattern: ./nodered:/data is a bind mount (settings.js, # flows.json, start.sh, package.json are host-owned); /data/node_modules # is an anonymous volume so the bind mount doesn't clobber the installed # deps. Docker copies the image's node_modules into the volume on first start. FROM nodered/node-red:5.0.0 # Install Dashboard 2.0 (and any future deps) at image-build time. # Versions are pinned in infra/nodered/package.json for reproducibility. # The NR base image defaults USER to node-red (uid 1000), so switch to root # for the install (writes to /data/node_modules need root then chown). USER root COPY package.json /data/package.json WORKDIR /data RUN npm install --omit=dev --no-audit --no-fund \ && chown -R 1000:1000 /data/node_modules \ && echo "[Dockerfile] installed @flowfuse/node-red-dashboard" # start.sh handles the SPA base-href patch (idempotent: grep guard). # Inline-write + chmod so we don't hit the COPY-then-chmod permission issue # (the base image's /data ACLs block chmod on COPY'd files even as root). RUN printf "%s\n" \ "#!/bin/sh" \ "INDEX=/data/node_modules/@flowfuse/node-red-dashboard/dist/index.html" \ "BASE_HREF=\"\${DASHBOARD_BASE_HREF:-/dashboard/}\"" \ "if [ -f \"\$INDEX\" ] && ! grep -q \"" \ " /data/start.sh \ && chmod +x /data/start.sh \ && chown 1000:1000 /data/start.sh # Drop back to NR's default user; userDir is /data via the compose volume mount. USER node-red WORKDIR /usr/src/node-red