Technical Communicator: Maria Sivenkova
Technical Reviewer: Lazuardi N Putra
Last modified: July 3, 2026
Why This Guide Exists
n8n is one of the most widely deployed open-source workflow automation platforms in the world. Its fair-code model and powerful node-based editor have made it a favourite among engineering and operations teams who need to connect apps, APIs, and services without writing full-stack code. But its popularity also makes it a target.
Like any actively developed open-source project, n8n ships patches regularly, including fixes for vulnerabilities that carry real risk if left unaddressed. The catch is that updating n8n safely is never just a one-liner. Self-hosted deployments vary widely: npm on bare metal, Docker, Docker Compose, Kubernetes, Helm, managed cloud instances. And each demands its own update procedure, rollback path, and audit trail.
This guide was built from Autobahn Security’s Cyber Fitness Workout for n8n — a structured, step-by-step remediation framework used by security teams managing real-world patch workflows. Whether you are responding to an active CVE or conducting routine maintenance, following this process will help you:
- Verify your environment before touching anything
- Detect signs of compromise before the update erases evidence
- Back up the right things at the right time
- Apply the patch using the method that matches your deployment
- Confirm the remediation actually worked
⚠️ Before you begin: Replace every instance of <VERSION> throughout this guide with the minimum patched version identified in the vendor advisory for the specific CVE you are remediating — or with the latest stable n8n release when the advisory instructs upgrading beyond the minimum fixed release.
What Is at Stake: Known Risk Classes
Unpatched versions of n8n may expose your environment to the following vulnerability classes. The specific CVEs in scope for any given patch will vary — consult the vendor advisory to confirm which risk classes apply to your current version.
- 🔴 Remote Code Execution
- 🔴 Sandbox Bypass
- 🟠 Arbitrary File Read / Write
- 🟠 Information Disclosure
- 🟡 Cross-Site Scripting (XSS)
- 🟡 Improper Input Validation
- 🟡 Denial of Service
The severity and exploitation class of the specific CVE in scope determines whether emergency containment is required before the update — particularly if your n8n instance is internet-exposed or handles sensitive credentials and workflow data.
Step 0: Before You Begin — Three Advisory Checks
Complete these three checks using the vendor advisory before making any system changes.
1. Identify the minimum patched version
Use this version as the baseline for confirming that the planned update fully remediates the specific CVE. This becomes your <VERSION> throughout the guide.
2. Identify the affected component
Confirm whether the vulnerability affects n8n core, a built-in node, workflow execution, expression handling, public endpoints, or a bundled dependency.
3. Confirm severity and exploitation class
Use this to decide whether emergency containment steps are required before patching. RCE or sandbox bypass vulnerabilities on internet-exposed instances may require immediate isolation.
Pre-Patch Integrity Check
Complete this check before upgrading. If skipped, document the reason with a justification tied to exploitation class, exposure level, and business impact. Its purpose is to catch active compromise before the update destroys volatile evidence.
1. Check for unexpected n8n or Node.js processes
ps aux | grep -E 'n8n|node' | grep -v grep
Review the output for unexpected shells, child processes, crypto miners, reverse shells, or commands that do not match normal n8n execution.
2. Review recent n8n service logs
npm / systemd:
journalctl -u n8n --since "48 hours ago" | grep -iE 'login|auth|fail|error|exec|shell|permission'
Docker:
docker logs <container_name> --since 48h | grep -iE 'login|auth|fail|error|exec|shell|permission'
Kubernetes:
kubectl logs -n <namespace> deploy/<deployment_name> --since=48h | grep -iE 'login|auth|fail|error|exec|shell|permission'
⛔ Halt and escalate if logs show unauthorized access, suspicious execution patterns, or unexplained errors — stop here before continuing.
3. Preserve logs before restarting or replacing n8n
Restarting, replacing, or rescheduling n8n may remove volatile evidence. Save logs first.
npm / systemd:
journalctl -u n8n --since "48 hours ago" > /tmp/n8n-systemd-logs-$(date +%Y%m%d).txt
Docker standalone:
docker logs <container_name> > /tmp/n8n-container-logs-$(date +%Y%m%d).txt
Docker Compose:
docker compose logs n8n > /tmp/n8n-compose-logs-$(date +%Y%m%d).txt
Kubernetes:
kubectl logs -n <namespace> deploy/<deployment_name> > /tmp/n8n-k8s-logs-$(date +%Y%m%d).txt
Confirm the saved log file is non-empty before proceeding.
How to Choose the Right Remediation Option
Option 1 — npm host-managed upgrade: Choose this if n8n was installed with npm and runs directly on a host through Node.js, systemd, PM2, or another process supervisor.
Option 2 — Docker container-managed upgrade: Choose this if n8n runs as a Docker container or Docker Compose service that you manage directly.
Option 3 — Platform-managed upgrade: Choose this if n8n runs through Kubernetes, Helm, n8n Cloud, a marketplace image, a PaaS provider, or a managed service provider.
📌 Downtime note: Updating n8n requires replacing or restarting the running application process.
Plan a maintenance window — active workflow executions, webhook handling, editor sessions, and queued jobs may be interrupted during the upgrade.
Expected service impact by deployment model:
- npm or Node.js self-hosted deployments require stopping and restarting the n8n process or service
- Docker standalone deployments require replacing the running n8n container
- Docker Compose deployments require recreating the n8n service and may also affect dependent services if the full stack is restarted
- Kubernetes or Helm deployments usually perform a rolling pod replacement, but brief interruption can still occur for in-flight executions, webhooks, or single-replica workloads
- n8n Cloud or managed deployments may trigger an instance restart during the version change
Preparation Checks (All Deployment Types)
Complete these five checks before opening the change window, regardless of your deployment model.
1. Confirm operational health
systemd:
systemctl status n8n --no-pager
Docker standalone:
docker ps --filter "name=<container_name>" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
Docker Compose:
docker compose ps n8n
Kubernetes:
kubectl get pods -n <namespace> -o wide | grep -i n8n
kubectl rollout status deployment/<deployment_name> -n <namespace>
Do not start the upgrade against a degraded or repeatedly restarting instance unless this is an emergency recovery.
2. Confirm and record the current running version
npm:
n8n --version
Docker standalone:
docker exec <container_name> n8n --version
# Fallback if container is unavailable:
docker inspect <container_name> --format '{{.Config.Image}}'
Docker Compose:
docker compose exec n8n n8n --version
# Fallback:
docker compose config | grep image
Kubernetes:
kubectl exec -n <namespace> deploy/<deployment_name> -- n8n --version
# Fallback:
kubectl get deploy/<deployment_name> -n <namespace> -o jsonpath='{.spec.template.spec.containers[*].image}'
3. Confirm the installation method
# Check for global npm installation
npm list -g n8n --depth=0
# Check for Docker standalone container
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}" | grep -i n8n
# Check for Docker Compose service
docker compose ps n8n
# Check for Kubernetes workload
kubectl get deployments,pods -A | grep -i n8n
# Check for Helm release
helm list -A | grep -i n8n
4. Confirm the data location and database backend
Confirm the following configuration items before backing up or upgrading:
DB_TYPEN8N_USER_FOLDERN8N_ENCRYPTION_KEY— confirm where it is defined and that n8n can access it; do not record the key value- PostgreSQL host, database name, username, and Secret reference if
DB_TYPE=postgresdb - Mounted volume, bind mount, or persistent volume claim used for n8n state
npm — check default user folder:
ls -la ~/.n8n
printenv N8N_USER_FOLDER
systemd:
systemctl cat n8n
Docker Compose:
docker compose config
Kubernetes:
kubectl describe deployment <deployment_name> -n <namespace>
5. Back up the n8n user folder or persistent volume
npm — default user folder:
tar -czf /tmp/n8n-user-folder-$(date +%Y%m%d).tar.gz ~/.n8n
npm — custom user folder:
tar -czf /tmp/n8n-user-folder-$(date +%Y%m%d).tar.gz <n8n_user_folder>
Docker named volume:
docker run --rm -v <volume_name>:/data:ro -v /tmp:/backup alpine \
tar -czf /backup/n8n-user-folder-$(date +%Y%m%d).tar.gz /data
Docker bind-mount:
tar -czf /tmp/n8n-user-folder-$(date +%Y%m%d).tar.gz <host_bind_mount_path>
📌 SQLite note: If the instance uses SQLite, stop n8n before backing up — or ensure the backup captures the database file along with any active -wal and -shm sidecar files.
Remediation Option 1: npm Host-Managed Upgrade
Use this option if n8n was installed with npm and runs directly on a host through systemd, PM2, or another process supervisor.
Step 1: Stop the active n8n service
systemd:
sudo systemctl stop n8n
PM2:
pm2 stop n8n
Confirm no n8n process remains active:
pgrep -af 'n8n|node.*n8n'
Step 2: Confirm Node.js support and install the target version
# Check active Node.js version (must be >=18.17 and <=22)
node --version
# Record the active npm global prefix before installing
npm config get prefix
# Install the target patched version
sudo npm install -g n8n@<VERSION>
📌 If the command fails with EACCES, do not change ownership recursively or force permissions. Rerun using your organization’s approved Node.js package management method. If n8n was installed under nvm, a service account, or a custom npm prefix, run the install from the same runtime context used by the n8n service.
Step 3: Validate the installed binary version
n8n --version
# If version does not match <VERSION>, check the active binary location:
which n8n
Step 4: Restart n8n through the process supervisor
systemd:
sudo systemctl start n8n
sudo systemctl status n8n --no-pager
PM2:
pm2 start n8n
pm2 describe n8n
Step 5: Verify the running service after restart
systemd startup logs:
journalctl -u n8n --since "5 minutes ago" --no-pager | grep -iE 'ready|version|migration|error|failed'
PM2 logs:
pm2 logs n8n --lines 50 --raw
Validate local web service (replace 5678 if using a custom port):
curl -I http://localhost:5678
Remediation Option 2: Docker Container-Managed Upgrade
Option 2.1 — Docker Standalone
Step 1: Pull the target image
# Record the currently configured image
docker inspect <container_name> --format '{{.Config.Image}}'
# Pull the target version
docker pull <n8n_image>:<VERSION>
# Validate it is available locally
docker image inspect <n8n_image>:<VERSION> --format '{{.Id}}'
Step 2: Identify the running container
docker ps --filter "ancestor=<current_n8n_image>" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
# If no result, list all containers:
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
Step 3: Export the current container runtime configuration
BLUEPRINT="/tmp/n8n-docker-blueprint-$(date +%Y%m%d).txt"
docker inspect <container_name> --format 'IMAGE: {{.Config.Image}}
ENV: {{json .Config.Env}}
MOUNTS: {{json .Mounts}}
PORTS: {{json .HostConfig.PortBindings}}
RESTART: {{json .HostConfig.RestartPolicy}}
NETWORK: {{.HostConfig.NetworkMode}}
USER: {{.Config.User}}
LABELS: {{json .Config.Labels}}
ENTRYPOINT: {{json .Config.Entrypoint}}
CMD: {{json .Config.Cmd}}' > "$BLUEPRINT"
# Validate blueprint was created
test -s "$BLUEPRINT" && sed -n '1,20p' "$BLUEPRINT"
⚠️ Security note: The exported environment block may contain secrets. Store the blueprint securely and do not paste secret values into the change record.
Step 4: Stop and preserve the old container
docker stop <container_name>
docker rename <container_name> <container_name>_old_rollback
# Confirm the rollback container is preserved
docker ps -a --filter "name=<container_name>_old_rollback" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
Step 5: Start a new container from the updated image
docker run -d --name <container_name> <existing_runtime_flags> <n8n_image>:<VERSION>
Before running, confirm <existing_runtime_flags> includes: volume or bind mount for /home/node/.n8n, environment variables or --env-file, database settings, N8N_ENCRYPTION_KEY source, port mappings, restart policy, network settings, configured user, labels, and startup command.
Step 6: Validate the upgraded container
# Review startup logs
docker logs <container_name> --since 5m
# Confirm the container is running
docker ps --filter "name=<container_name>" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
# Validate version
docker exec <container_name> n8n --version
# Validate web service
curl -I http://localhost:5678
# Confirm rollback container is still available
docker ps -a --filter "name=<container_name>_old_rollback" --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
After the patched container is verified and the rollback window has ended, remove the preserved rollback container if change control allows it:
docker rm <container_name>_old_rollback
Option 2.2 — Docker Compose
Step 1: Back up and review the Compose configuration
cp docker-compose.yml docker-compose.yml.backup.$(date +%Y%m%d)
# Render the effective Compose configuration
docker compose config > /tmp/n8n-compose-config-$(date +%Y%m%d).yaml
Step 2: Update the n8n image tag
Open docker-compose.yml and update only the n8n image tag:
image: <n8n_image>:<VERSION>
# Validate the rendered config shows the expected image
docker compose config | grep -i '<n8n_image>:<VERSION>'
Step 3: Pull the updated n8n image
docker compose pull n8n
docker compose images n8n
Step 4: Recreate the n8n service
docker compose up -d --no-deps n8n
docker compose ps n8n
Step 5: Validate the upgraded Compose service
docker compose logs n8n --tail=100
docker compose exec n8n n8n --version
curl -I http://localhost:5678
Confirm logs do not show database migration failures, SQLITE_BUSY locking errors, EACCES permission errors, missing environment variables, credential decryption errors, or repeated restarts.
Remediation Option 3: Platform-Managed Upgrade
Option 3.1 — Kubernetes (Helm)
Step 1: Identify the active Helm release and export live values
helm list -A | grep -i n8n
# Export user-supplied live values
helm get values <release_name> -n <namespace> -o yaml > /tmp/n8n-live-values-$(date +%Y%m%d).yaml
# Export fully computed values for rollback context
helm get values <release_name> -n <namespace> -a -o yaml > /tmp/n8n-computed-values-$(date +%Y%m%d).yaml
# Validate files are present and non-empty
test -s /tmp/n8n-live-values-$(date +%Y%m%d).yaml
test -s /tmp/n8n-computed-values-$(date +%Y%m%d).yaml
Step 2: Resolve the chart version and n8n application version
helm show chart <n8n_chart_reference> --version <CHART_VERSION> | grep -E 'version|appVersion'
# List available chart versions
helm search repo <chart_name> --versions | head
📌 Helm chart versions and n8n application versions are separate versioning schemes. Confirm appVersion matches the required n8n <VERSION> before upgrading.
Step 3: Upgrade the Helm release
# Dry run first
helm upgrade <release_name> <n8n_chart_reference> \
--version <CHART_VERSION> \
-n <namespace> \
-f /tmp/n8n-live-values-$(date +%Y%m%d).yaml \
--dry-run
# Apply the upgrade
helm upgrade <release_name> <n8n_chart_reference> \
--version <CHART_VERSION> \
-n <namespace> \
-f /tmp/n8n-live-values-$(date +%Y%m%d).yaml \
--wait
Step 4: Validate rollout state and running image
kubectl get deployment -n <namespace> -l app.kubernetes.io/name=n8n
DEPLOYMENT_NAME="<deployment_name>"
kubectl rollout status deployment/$DEPLOYMENT_NAME -n <namespace>
kubectl get pods -n <namespace> -l app.kubernetes.io/name=n8n \
-o jsonpath='{.items[*].spec.containers[*].image}'
Step 5: Validate the upgraded Kubernetes service
kubectl logs -n <namespace> deployment/$DEPLOYMENT_NAME --tail=100
kubectl exec -n <namespace> deployment/$DEPLOYMENT_NAME -- n8n --version
kubectl get pods -n <namespace> -l app.kubernetes.io/name=n8n
# Validate via port-forward
kubectl port-forward -n <namespace> deployment/$DEPLOYMENT_NAME 5678:5678
In a second terminal, confirm the service responds:
curl -I http://localhost:5678
Confirm logs do not show database migration failures, missing environment variables, credential decryption errors, persistent volume permission errors, image pull errors, or repeated restarts.
Option 3.2 — n8n Cloud or Managed Provider
Step 1: Confirm upgrade authority and target version
Before the change begins, request confirmation of the following from the upgrade owner or provider:
- Upgrade owner (who has permission to change the n8n version)
- Target n8n version
- Expected restart impact
- Rollback path
- Post-upgrade evidence source
Step 2: Apply the update through the provider or n8n Cloud dashboard
For n8n Cloud: sign in with the required owner account, open the target instance in the cloud admin dashboard, select the target patched version or Latest Stable, save the change, and confirm the restart.
For managed providers: request or trigger the provider-approved update workflow for the target patched version.
⚠️ Do not attempt host-level commands unless the provider explicitly documents and supports them for the managed environment.
Step 3: Validate the managed instance after restart
curl -I https://<n8n_instance_url>
Collect evidence for the change record:
- Screenshot or provider output showing the updated n8n version
- Confirmation the instance is running the minimum patched version or later
- Confirmation that workflow execution, webhooks, and editor access were tested after restart
- Confirmation that no provider-side rollback, failed update, degraded state, or incident is active
One more automation risk reduced. Patching n8n is not just a software update — it is a critical step in protecting the workflows, credentials, and integrations your team depends on every day. Whether you run npm on a single server or a multi-replica Kubernetes cluster, following this structured approach means you patch with confidence, preserve evidence if something looks wrong, and always have a verified path to roll back.
Want to see your organization’s full vulnerability exposure — not just n8n? Autobahn Security’s platform continuously maps your attack surface, prioritizes what matters most, and turns findings into guided workouts like this one.
About the team behind this guide
Maria Sivenkova is Senior Technical Communicator at Autobahn Security, where she writes about vulnerability management and cybersecurity. She holds a PhD in Communications and has over a decade of experience in the field, with a growing focus on responsible AI governance.
Lazuardi N. Putra is a Senior DevOps Engineer at Autobahn Security with 10+ years of expertise in infrastructure automation, containerization, and cloud platforms. He is a Certified Kubernetes Administrator (CKA) and Certified Kubernetes Security Specialist (CKS).