# ============================================================================== # Dockerfile – n8n on Hugging Face Spaces # # Solves: # 1. "Command not found" → uses node:22-alpine base (has /bin/sh + bash) # 2. "Permissions too wide" → start.sh fixes perms before dropping to uid 1000 # 3. Port 7860 → HF Spaces requirement # 4. Persistence → /data/.n8n backed by HF persistent storage # 5. Node >= 22.16 → required by n8n >= 2.15 and isolated-vm >= 6.1 # ============================================================================== # --------------- Stage 1: Build (includes native compilation toolchain) ------- FROM node:22-alpine AS build # Install native build dependencies for isolated-vm and other C++ addons RUN apk add --no-cache \ python3 \ make \ g++ # Install n8n globally (semver installed explicitly to avoid module resolution # issues when running via su-exec in the runtime stage) RUN npm install -g semver n8n@latest --omit=dev && \ npm cache clean --force # --------------- Stage 2: Runtime (lean, no build toolchain) ------------------ FROM node:22-alpine # Runtime-only dependencies RUN apk add --no-cache \ su-exec \ tini \ bash \ curl \ git \ openssh-client \ ca-certificates \ tzdata # Copy the globally-installed n8n from the build stage COPY --from=build /usr/local/lib/node_modules /usr/local/lib/node_modules # Re-link the n8n binary from source (the copied symlink may be broken) RUN ln -sf /usr/local/lib/node_modules/n8n/bin/n8n /usr/local/bin/n8n # Note: node:22-alpine already provides user "node" at uid/gid 1000. # No addgroup/adduser needed — start.sh uses su-exec 1000:1000. # ---------- Environment ------------------------------------------------------- # NODE_PATH tells Node where to find globally-installed modules (fixes # "Cannot find module 'semver/functions/satisfies'" at runtime) ENV NODE_PATH=/usr/local/lib/node_modules \ N8N_PORT=7860 \ N8N_PROTOCOL=https \ N8N_USER_FOLDER=/data/.n8n \ N8N_DIAGNOSTICS_ENABLED=false \ N8N_HIRING_BANNER_ENABLED=false \ N8N_PERSONALIZATION_ENABLED=false \ NODE_ENV=production \ GENERIC_TIMEZONE=UTC # Expose the HF-required port EXPOSE 7860 # ---------- Entrypoint -------------------------------------------------------- COPY start.sh /start.sh RUN chmod +x /start.sh # Use tini as PID 1 to reap zombies and forward signals ENTRYPOINT ["/sbin/tini", "--"] CMD ["/start.sh"]