How to Downgrade n8n in Docker: Rollback Guide with db:revert (2026)

Introduction

Downgrading n8n in Docker is the move nobody plans and everybody eventually needs. An update ships, a migration runs, and suddenly a production workflow throws errors it never threw before, or the container refuses to start at all. Changing the image tag back looks like the obvious fix, and it is half of the fix. The other half is the database: if the newer version ran a database migration, the older n8n binary wakes up on a schema it does not understand. This guide walks through the complete rollback for Docker and Docker Compose installs, backup checklist, migration revert order, tag swap, and verification, plus the nuclear option for when the clean path fails.

Quick answer

To downgrade n8n in Docker, stop the stack, run n8n db:revert on the current version for each database migration, pin the older image tag, and restart. If db:revert reports an irreversible migration, restore your pre-upgrade database backup instead.

Evidence

Three facts set the stage, all current as of 22 October 2026.

First, n8n moves fast enough that rollbacks are a when, not an if. The official documentation states that n8n “releases a new minor version most weeks,” and the Docker installation page checked on 22 October 2026 listed stable 2.44.5 alongside beta 2.45.2. A weekly release cadence means any self-hosted instance accumulates migration-bearing updates constantly, so the rollback path below is a core operational skill, not an edge case.

Second, the vendor’s own words define the only supported rollback procedure. n8n’s installation documentation, in a section literally titled Reverting an upgrade, says: “Install the older version that you want to go back to. If the upgrade involved a database migration: 1. Check the feature documentation and release notes to see if there are any manual changes you need to make. 2. Run n8n db:revert on your current version to roll back the database. If you want to revert more than one database migration, you need to repeat this process.” This passage is the official downgrade contract, and every step of this guide maps to it.

Third, the order is enforced by the code itself, not just the docs. The actual db:revert implementation in the n8n repository reverts exactly the last executed migration per run (undoLastMigration), and if you already swapped the image to the old version before reverting, it refuses with an explicit instruction: “Please upgrade n8n again and run n8n db:revert and then downgrade again.” If the last migration has no down step, the same code aborts with: “Cancelled command. The last migration … was irreversible.” In other words, revert first on the new version, then downgrade, or do not downgrade at all.

The scale of the project explains why migrations are the risk center. n8n’s GitHub repository held 206,739 stars and the official image had accumulated 268,155,952 pulls on Docker Hub as of early October 2026, and migrations ship inside that volume of releases constantly. The official update guide therefore pushes a monthly update cadence to keep jumps small: “Update frequently: this avoids having to jump multiple versions at once, reducing the risk of a disruptive update.” Small jumps also mean short revert chains when something does go wrong. For the wider self-hosting picture, including installation, backup strategy, and when to pick Docker over npm, our complete n8n guide is the starting point, and the companion update guide covers the forward direction of this same operation.

Prerequisites

  • A Docker or Docker Compose n8n install that you have shell access to.
  • Know both versions involved: the currently running (bad) version and the target (good) version. Run docker images | grep n8n or check the version in the editor footer, and write both down.
  • The pre-upgrade backup from the bad update, or the ability to take one now. The backup and restore tutorial covers the full routine if you skipped it.
  • Ten quiet minutes with no critical workflows running, because the rollback stops n8n.

Step-by-step guide

Step 0, Read the release notes between your versions

Before touching anything, open the n8n releases on GitHub and scan every version between your target and the current one. You are hunting for two things: database migrations (phrased as “migrated X to Y” or “database migration”) and breaking changes. The release notes for risky releases have historically carried explicit rollback instructions. n8n 1.43.0’s notes, for example, included a “Rolling back to a previous version” section for self-hosted users that said to delete any RBAC projects created and “Revert the database migrations using n8n db:revert,” as recorded in the official release notes. If the notes for your jump mention irreversible migrations, skip the db:revert path and go straight to the backup restore in Step 5.

Step 1, Back up everything, even though you are going backwards

A rollback is not a backup. If db:revert hits an irreversible migration, or the old version boots and corrupts something, the pre-rollback backup is your only way back. Run all of these:

# Where the rollback kit lives
mkdir -p ~/n8n-rollback-backup/{workflows,credentials}

# Export workflows and credentials through the running (new) container
docker compose exec n8n n8n export:workflow --all --output=/home/node/.n8n/rollback-tmp/workflows/
docker compose exec n8n n8n export:credentials --all --output=/home/node/.n8n/rollback-tmp/credentials/
docker compose cp n8n:/home/node/.n8n/rollback-tmp ~/n8n-rollback-backup/
chmod 600 ~/n8n-rollback-backup/rollback-tmp/credentials.json

# Database dump (Compose with Postgres)
docker compose exec db pg_dumpall -U n8n > ~/n8n-rollback-backup/db-$(date +%F).sql

# Database file (SQLite installs): copy the live file
cp ~/.n8n/database.sqlite ~/n8n-rollback-backup/database-$(date +%F).sqlite

# Full data-volume tarball, the actual nuclear option
docker run --rm -v n8n_n8n_data:/data -v ~/n8n-rollback-backup:/backup \
  alpine tar czf /backup/n8n-data-$(date +%F).tgz -C /data .

Replace n8n_n8n_data with your real volume name from docker volume ls; the name carries your Compose project prefix. Copy the .env or docker-compose.yml too, and confirm you have the encryption key: every credential is encrypted with N8N_ENCRYPTION_KEY, and a database backup without the key cannot decrypt a single credential. Our Docker Compose setup guide explains where the key lives and why it must travel with every backup.

Step 2, Revert the migrations on the CURRENT version

This is the step everyone gets backwards, and the source code quoted above shows why it matters: db:revert must run against the newer version, because only the newer binary contains the migration code needed to undo it. Stop n8n first, then run the command in a one-off container:

# Stop n8n so no executions or connections interfere
docker compose stop n8n

# Revert the most recent migration; repeat once per migration
docker compose run --rm n8n n8n db:revert

Each run undoes exactly one migration. Check how many migrations the bad upgrade applied by scanning the startup logs from that upgrade:

docker compose logs n8n | grep -i "starting migration"

Every Starting migration <name> line is one db:revert run you owe. Repeat the command for each one. When you are unsure whether more remain, one extra run is safe: on a database whose migrations are all reverted, the command cancels itself with “Cancelled command. The database was never migrated.” rather than doing damage.

Two failure modes here are informative, not fatal. If the command says the last migration “was irreversible,” stop: db:revert cannot walk past it, and forcing the downgrade anyway leaves the schema in a state neither version understands. Jump to Step 5. If it says you downgraded before reverting and tells you to upgrade again, that means the current image is already the old one; restore the newer tag temporarily, revert, then swap back.

For plain docker run installs (no Compose), the same logic applies through docker exec on a stopped-then-restarted container. The official CLI documentation gives the Docker invocation pattern as docker exec -u node -it <n8n-container-name> <n8n-cli-command>; in practice, stop the container, recreate it with the same volume and the new image, run docker exec -u node n8n n8n db:revert (repeating per migration), and stop it again before Step 3.

Step 3, Pin the older image tag and restart (Docker Compose)

Only now, with the schema matching what the old version expects, swap the tag. Edit docker-compose.yml (or your .env if the tag is parameterized) and set the exact version:

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:2.43.8

Use an exact tag, never latest, so the rollback is reproducible and the next docker compose pull does not silently re-apply the bad version. Then:

# Make sure the old image is present locally
docker compose pull n8n

# Recreate the container on the old tag
docker compose up -d

# Watch the startup: version line, then migrations must NOT run
docker compose logs --tail=50 n8n | grep -iE "version|migration"

A clean rollback shows the old version banner and no migration lines. If you see “Migrations in progress, please do NOT stop the process,” something is off: the old version is trying to migrate the schema again, which means a migration was missed in Step 2. Stop the container immediately and revert the remaining migration before restarting.

Step 4, The plain docker run variant

For installs started with docker run rather than Compose, the official Docker update flow is stop, remove, and start again, which the rollback mirrors with the old tag:

docker stop n8n && docker rm n8n

docker run -d \
  --name n8n \
  -p 5678:5678 \
  -e GENERIC_TIMEZONE="Asia/Shanghai" \
  -e TZ="Asia/Shanghai" \
  -v n8n_data:/home/node/.n8n \
  docker.n8n.io/n8nio/n8n:2.43.8

Keep every flag identical to the original run, especially the volume mount. The volume is where the data, the config with the encryption key, and the (now reverted) SQLite database live; recreating the container without it is how people “lose” all their workflows, a failure mode the community has debugged repeatedly.

Step 5, Verify the rollback before declaring victory

A rollback is not done when the container starts; it is done when the data proves intact.

# 1. The running binary is the old version
docker compose exec n8n n8n --version
# 2.43.8

# 2. The editor loads and the owner account works
curl -s -o /dev/null -w "%{http_code}\n" http://localhost:5678/
# 200

Then open the editor and check three things by hand: a workflow opens and its nodes render (a version gap can leave unknown node types as gray boxes), one credential decrypts (open any credential and confirm the fields populate instead of showing a decryption error), and a manual execution of your most critical workflow completes. If a credential fails to decrypt, the encryption key changed or the volume mount is wrong; fix the key before touching anything else.

Step 6, The nuclear option: restore the pre-upgrade backup

When db:revert reports an irreversible migration, or you are crossing a major version boundary, the documented path ends and the backup path begins. Restore the database into the stack running the old tag:

# Stop everything first
docker compose down

# Postgres: wipe and reload the pre-upgrade dump
docker compose up -d db
sleep 10
docker compose exec -T db psql -U n8n -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
cat ~/n8n-rollback-backup/db-2026-10-21.sql | docker compose exec -T db psql -U n8n

# SQLite or full volume restore: put the tarball back
docker run --rm -v n8n_n8n_data:/data -v ~/n8n-rollback-backup:/backup \
  alpine sh -c "rm -rf /data/* && tar xzf /backup/n8n-data-2026-10-21.tgz -C /data"

# Now bring n8n up on the OLD tag (docker-compose.yml already pinned in Step 3)
docker compose up -d

The volume restore replaces the entire /home/node/.n8n directory, including the SQLite database, the config file with the encryption key, and logs, so key mismatches cannot happen. This is the only safe cross-major rollback. After restore, run the Step 5 verification in full.

Troubleshooting

The container crashes on startup right after the tag swap

This is the signature of reverting in the wrong order, or not reverting at all. The old binary reads a schema with tables or columns it never created and dies during initialization. Fix: change the tag back to the newer version, run docker compose up -d, let it start cleanly, then follow Step 2 properly (stop, db:revert per migration), and only then swap the tag back. The db:revert source literally encodes this recovery: “Please upgrade n8n again and run n8n db:revert and then downgrade again.”

db:revert says the last migration was irreversible

Stop. This is the command telling you the clean path does not exist. Do not downgrade anyway; the schema will sit between versions and neither binary will be happy. Go to Step 6 and restore the pre-upgrade database backup. If you do not have one, your remaining options are exporting workflows and credentials from the running new version (Step 1’s export commands still work on the bad version) and rebuilding on a fresh old-version install.

The container loops on a migration failure after the bad update

Real example from the n8n community forum: after pulling n8n:stable, a user’s container looped with Migration "AddActiveVersionIdColumn1763047800000" failed, error: insert or update on table "workflow_entity" violates foreign key constraint. A failed migration leaves the database half-migrated, and downgrading a half-migrated database is not supported by db:revert at all. Restore the pre-upgrade backup (Step 6), then upgrade again one minor version at a time to isolate which release breaks, instead of re-pulling the same tag and hoping.

Credentials show decryption errors after the rollback

The database restored fine, but the encryption key does not match. This happens when the rollback recreated or replaced the /home/node/.n8n/config file, or when the stack now mounts a different volume than before. Confirm the config file on the volume contains the same encryptionKey you backed up, and that N8N_ENCRYPTION_KEY is either unset (so the file is used) or set to the identical value. If you set the key as an environment variable, a typo’d key produces exactly this symptom on every credential at once.

Executions and workflow versions from the bad period are gone

Expected, not a bug. db:revert only touches schema; executions that ran while the bad version was live stay in the tables. But if you used the nuclear option, the restored backup contains only data up to the backup timestamp: workflows edited and executions recorded after the backup are gone. This is the price of skipping Step 1 before the original update, and the reason the backup checklist lives at the top of this guide rather than at the bottom.

FAQ

The six questions above cover the recurring doubts; the short version of all of them is that the database is the boss. db:revert exists because n8n’s own team needed it: release notes for risky versions shipped rollback instructions around it, and the implementation enforces the correct order. Respect the order, keep the backups, and downgrades stay boring.

Conclusion

Downgrading n8n in Docker is a two-phase operation, and the phases have a fixed order: revert the database migrations with n8n db:revert while still on the new version, then pin the older image tag and restart. Most rollbacks end there. When db:revert reports an irreversible migration or you are crossing a major version line, the backup restore in Step 6 is the only honest path. Pair this guide with the complete n8n guide for the broader self-hosting picture, and consider pinning exact image tags permanently, as our Docker Compose setup recommends, so the next bad update is one tag change away from a fix instead of a scramble.

Frequently asked questions

Can I downgrade n8n across major versions, for example from 2.x back to 1.x?

Almost never through db:revert alone. Major versions ship migrations whose down steps cannot reconstruct removed columns, and some migrations are outright irreversible. If you must go across a major boundary, restore your pre-upgrade database backup into the stack running the old image tag, then verify workflows and credentials. Treat db:revert as a same-major or one-minor tool.

How do I know how many times to run n8n db:revert?

Each run undoes exactly one migration, the most recently executed one. n8n prints Starting migration <name> in the container logs at every upgrade, so scan your logs after the bad update and count the migration lines between the old and new versions. Run db:revert once per migration, and stop when the log shows the migrations that shipped with your target version.

Does db:revert delete my workflows or execution history?

It reverts schema changes, not rows: your workflows, credentials, and executions stay in place. But a migration that added a column and then got reverted takes that column and its data with it, so anything stored in a new-format column after the bad upgrade can be lost. This is why the full backup in Step 1 is non-negotiable even when you plan to use db:revert.

Do I have to stop n8n before running n8n db:revert?

Yes. Stop the n8n container first, then run the command in a one-off container with docker compose run --rm n8n n8n db:revert so nothing holds open database connections while the schema changes. Reverting against a running instance risks lock timeouts and a half-reverted schema, which is worse than the problem you started with.

Is there an official Docker downgrade guide from n8n?

No. n8n publishes no dedicated Docker downgrade page. The official guidance lives in the Reverting an upgrade section of the npm installation docs and in release notes that carried rollback instructions for risky releases. The mechanics in this guide follow that same documented logic, adapted to Docker by running the CLI command inside a container instead of on the host.

What if the downgraded n8n starts but my workflows behave differently?

Then the bad upgrade changed behavior, not just the schema. Roll the workflows back too: import the pre-upgrade workflow export files from your backup over the current versions, or restore the full database backup. Workflow definitions live in the database, so a schema-only rollback does not undo edits made while the new version was running.

TP
ToolPilot Team

We install, break, and fix automation tools so you don't have to. Every tutorial on ToolPilot is tested hands-on before publishing.

Keep reading