Who this is for: Platform engineers and DevOps teams deploying n8n in Docker‑based production environments. We cover this in detail in the n8n Security & Hardening Guide.
Quick Diagnosis
Problem – n8n containers are often launched with convenience defaults (e.g., root user, latest tag, mounted Docker socket). Those settings let an attacker read/write workflow data, run arbitrary commands, or pivot to the host.
Solution – If any item in the checklist below is unchecked—especially “container runs as root”, “Docker socket mounted”, or “plain‑text secrets”—stop the container, apply the hardening steps, and redeploy.
1. Why Docker Misconfigurations Matter for n8n?
If you encounter any Role Based Access Control pitfalls resolve them before continuing with the setup.
| Misconfiguration | Direct impact on n8n | Typical symptom |
|---|---|---|
| Running as root inside the container | Enables privilege escalation to the host via Docker socket or CVE‑2024‑XXXX | docker exec -u 0 … works, container logs show UID 0 |
| Using latest tag | Pulls untested image versions; may introduce breaking changes or known vulnerabilities | Unexpected API errors after a docker pull |
| Mounting /var/run/docker.sock | Gives the container full control over the Docker daemon → host compromise | docker ps works from inside the n8n container |
| Storing secrets in plain‑text ENV vars | Secrets are visible via docker inspect and in logs |
docker inspect <container> reveals POSTGRES_PASSWORD |
| No read‑only filesystem | Malicious workflow can write files to the container and later exfiltrate them | Unexpected files appear in /data after a workflow runs |
Missing resource limits (cpu, memory) |
Allows a crafted workflow to consume all host resources → denial‑of‑service | Host becomes unresponsive when a heavy workflow executes |
| No seccomp/AppArmor profile | Allows syscalls that can break out of the container sandbox | auditd shows execve of privileged binaries from n8n |
EEFA note – In production, each misconfiguration is a privilege‑escalation vector that can be chained with n8n’s “Execute Command” node. Attackers have repeatedly leveraged this to extract database credentials and spin up rogue containers.
2. Build‑time Hardening: Dockerfile Best Practices
| Goal | Dockerfile directive | Example | EEFA rationale |
|---|---|---|---|
| Run as non‑root | USER | USER node (or a dedicated UID/GID) |
Reduces impact of container breakout. |
| Pin base image | Fixed tag, not latest |
FROM node:18-alpine@sha256:… |
Guarantees reproducible builds and avoids unknown vulnerabilities. |
| Drop unnecessary packages | Multi‑stage build | See snippet below | Smaller attack surface, fewer binaries to exploit. |
| Set immutable filesystem | VOLUME only for required data | VOLUME /home/node/.n8n |
Prevents accidental writes to the code layer. |
| Remove build‑time secrets | Do not ARG secrets that persist |
ARG NPM_TOKEN → used only in RUN then unset |
Prevents leakage via image layers. |
2.1 Multi‑stage Dockerfile – Build stage
*Compile the application in a clean builder image and keep only the compiled output.*
FROM node:18-alpine@sha256:4c5b9e1f5a9c9f3b6d2c2e8b9f2e7a1c9d5f6a7b8c9d0e1f2a3b4c5d6e7f8g9h AS builder WORKDIR /src COPY package*.json ./ RUN npm ci --omit=dev COPY . . RUN npm run build
2.2 Multi‑stage Dockerfile – Runtime stage
*Run the compiled code as a low‑privilege user with a read‑only root filesystem.*
FROM node:18-alpine@sha256:4c5b9e1f5a9c9f3b6d2c2e8b9f2e7a1c9d5f6a7b8c9d0e1f2a3b4c5d6e7f8g9h WORKDIR /home/node/.n8n COPY --from=builder /src/dist ./dist ENV NODE_ENV=production N8N_PORT=5678 N8N_HOST=0.0.0.0 RUN addgroup -S n8n && adduser -S -G n8n -u 1001 n8n USER n8n EXPOSE 5678 CMD ["node", "dist/main.js"]
EEFA note – The
@sha256digest locks the base image to an immutable layer. If a CVE is disclosed for that exact digest, rebuild with an updated digest and redeploy. If you encounter any missing api rate limiting dos resolve them before continuing with the setup.
3. Runtime Hardening: Docker Run / Compose Settings
| Setting | Recommended flag/value | Why it matters |
|---|---|---|
| Read‑only root FS | –read-only | Prevents malicious writes to the container’s code layer. |
| User namespace mapping | –userns-remap=default (or custom) | Isolates container UID/GID from host. |
| Drop capabilities | –cap-drop ALL –cap-add CHOWN NET_BIND_SERVICE | Only needed capabilities remain. |
| Seccomp profile | –security-opt seccomp=./seccomp-n8n.json | Blocks syscalls used in privilege escalation. |
| AppArmor profile | –security-opt apparmor=profile-n8n | Enforces mandatory access control. |
| No Docker socket | Never mount /var/run/docker.sock |
Eliminates host‑Docker control from n8n. |
| Resource limits | –memory 512m –cpus 0.5 | Stops a rogue workflow from exhausting host resources. |
| Explicit network | –network n8n_net (isolated bridge) | Prevents accidental exposure to other services. |
| Healthcheck | –health-cmd “curl -f http://localhost:5678/healthz || exit 1” | Detects crashed containers early. |
| Log driver | –log-driver json-file –log-opt max-size=10m | Limits log growth, avoids DoS via log flooding. |
3.1 Hardened Docker Compose – Service definition (part 1)
*Core service settings, environment, and volume handling.*
services:
n8n:
image: myorg/n8n:1.45.0-alpine@sha256:4c5b9e1f5a9c9f3b6d2c2e8b9f2e7a1c9d5f6a7b8c9d0e1f2a3b4c5d6e7f8g9h
container_name: n8n
restart: unless-stopped
read_only: true
user: "1001:1001"
environment:
- N8N_HOST=0.0.0.0
- N8N_PORT=5678
3.2 Hardened Docker Compose – Service definition (part 2)
*Security options, resource limits, and secret integration.*
secrets:
- db_password
ports:
- "5678:5678"
networks:
- n8n_net
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5678/healthz"]
interval: 30s
timeout: 5s
retries: 3
security_opt:
- no-new-privileges:true
- seccomp=./seccomp-n8n.json
- apparmor=profile-n8n
cap_drop:
- ALL
cap_add:
- CHOWN
- SETGID
- SETUID
deploy:
resources:
limits:
cpus: "0.5"
memory: 512M
networks:
n8n_net:
driver: bridge
secrets:
db_password:
file: ./secrets/db_password.txt
EEFA note – The
read_only: trueflag is ignored if any volume is mounted with write permission. Keep data persistence to a *named volume* that is explicitly declaredread_only: falsefor the/home/node/.n8npath only.
4. Securing Secrets & Environment Variables
| Approach | Implementation | Pros | Cons |
|---|---|---|---|
| Docker secrets (Swarm) | docker secret create db_password ./db_password.txt |
Encrypted at rest, only exposed to container at runtime | Requires Swarm mode; not available in plain Docker Engine |
| Docker‑compose secrets | See docker-compose.yml above |
Works with Compose V3+; secret files are not stored in image layers | Files on host must be protected (chmod 600) |
| HashiCorp Vault / AWS Secrets Manager | Use n8n’s @n8n/vault node or custom credential type |
Centralized secret rotation, audit logging | Adds external dependency |
Environment variable file (.env) |
env_file: .env (but never commit to VCS) |
Simple for dev | Secrets appear in docker inspect if not masked |
4.1 Using Docker secrets with Swarm – Create the secret
*Store the password in the Swarm secret store; it never lives in the image.*
echo "SuperSecretPass!" | docker secret create n8n_postgres_password -
4.2 Deploy the stack with the secret attached
docker stack deploy -c docker-stack.yml n8n
4.3 Stack file excerpt – secret consumption
services:
n8n:
image: myorg/n8n:1.45.0-alpine
environment:
- DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/n8n_postgres_password
secrets:
- n8n_postgres_password
EEFA note – When using the
*_FILEpattern, ensure no duplicateDB_POSTGRESDB_PASSWORDenv var is set, otherwise the plain‑text value wins and defeats the secret‑by‑file protection. If you encounter any missing audit logging breach detection resolve them before continuing with the setup.
5. Monitoring, Auditing, and Automated Remediation
| Tool | What it checks | Example rule |
|---|---|---|
| Trivy (image scanner) | Known CVEs, misconfigurations (dockerfile and container) | trivy config --severity HIGH,CRITICAL . |
| Docker Bench for Security | CIS Docker Benchmark compliance | docker run -it –net host –pid host –cap-add audit_control -v /var/lib:/var/lib -v /etc:/etc -v /usr/bin/docker:/usr/bin/docker docker/docker-bench-security |
| Falco (runtime) | Detects suspicious syscalls (e.g., execve of /bin/sh from n8n) |
falco -c /etc/falco/falco.yaml |
| Prometheus + Grafana | Resource usage alerts (CPU, memory) | Alert when container_memory_usage_bytes > 0.8 * memory_limit |
| Open Policy Agent (OPA) Gatekeeper | Enforces admission policies (no latest, must use read‑only) |
Policy: deny if container.image.tag == "latest" |
5.1 OPA policy – deny latest tag
*Prevents accidental use of floating tags.*
package kubernetes.admission
deny[msg] {
img := input.request.object.spec.containers[_].image
endswith(img, ":latest")
msg := "Do not use the 'latest' tag for n8n images."
}
5.2 OPA policy – deny privileged containers
*Ensures n8n never runs with elevated capabilities.*
deny[msg] {
sec := input.request.object.spec.containers[_].securityContext
sec.privileged == true
msg := "Privileged containers are forbidden for n8n."
}
EEFA note – In a CI/CD pipeline, run
trivy config**before** pushing the image. If a misconfiguration is found, fail the pipeline and require a manual review. This prevents accidental drift into production.
TL;DR – Immediate Hardening Checklist
| Steps | Action | Command / File |
|---|---|---|
| 1 | Use a pinned, digest‑based base image | FROM node:18-alpine@sha256:… |
| 2 | Run as non‑root | USER 1001 in Dockerfile; --user 1001:1001 at runtime |
| 3 | Never mount /var/run/docker.sock |
Remove any -v /var/run/docker.sock:/var/run/docker.sock |
| 4 | Set container read‑only | docker run --read-only … or read_only: true in compose |
| 5 | Apply resource limits | --memory 512m --cpus 0.5 |
| 6 | Drop all capabilities, add only needed | --cap-drop ALL --cap-add CHOWN NET_BIND_SERVICE |
| 7 | Enable seccomp & AppArmor | --security-opt seccomp=seccomp-n8n.json --security-opt apparmor=profile-n8n |
| 8 | Store DB/passwords in Docker secrets | secrets: section in compose or docker secret create … |
| 9 | Add healthcheck | HEALTHCHECK CMD curl -f http://localhost:5678/healthz || exit 1 |
| 10 | Run image scanning (Trivy) and CIS benchmark (docker‑bench‑security) on CI | trivy config . ; docker run … docker-bench-security |
If any of the above rows are unchecked, stop the container, apply the fix, and redeploy.
This checklist reflects current best practices (as of 2026) for securing n8n deployments inside Docker. Re‑evaluate quarterly or after any major n8n release.



