How to Update Self-Hosted n8n in Docker Compose Without Losing Data (Tested 2.40 → 2.41)
Short on time? The n8n Production Self-Hosting Kit ($29) is the finished, tested version of what this tutorial builds.
Updating n8n is one command. Updating it safely is five, and the extra four are what save you when a release changes something you depend on.
We ran this exact procedure on September 30, 2026: a Docker Compose install with Postgres 17, a workflow and an encrypted credential, upgraded from n8n 2.40.6 to 2.41.4, then rolled back two different ways. The results are at the end.
Before you start: are you on a pinned version?
Open your docker-compose.yml. If the image line ends in :latest (or has no tag at all), you are not really choosing when you upgrade: any docker compose pull, server rebuild or a colleague's "quick fix" can jump you several versions at once.
Pin it through .env instead:
# docker-compose.yml
image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
# .env
N8N_VERSION=2.40.6
Now an upgrade is a one-line, reviewable change. (Our self-hosting tutorial and the free Docker Compose generator both set it up this way.)
To see which version you're running right now:
docker compose exec n8n n8n --version
Step 1: Pick the target version and read the release notes
On the day we tested, the Docker tags were latest = stable = 2.41.4 and next = beta = 2.42.1. For a production instance, upgrade to the current stable release, not next/beta.
Skim the release notes for every version between yours and the target (the n8n docs publish them per version). Look for "breaking", removed nodes, and changed environment variable defaults. For big jumps, go one minor version at a time.
Step 2: Back up the database and the encryption key
From the folder with your docker-compose.yml:
docker compose exec -T postgres pg_dump -U n8n -d n8n -Fc > n8n-before-upgrade.dump
cp .env env-before-upgrade.backup
The .env copy matters as much as the dump, because it holds N8N_ENCRYPTION_KEY. A database restored with the wrong key opens fine but every credential fails with "Credentials could not be decrypted". Move both files off the server (or at least out of the Docker volume) before continuing. Our backup tutorial covers doing this on a schedule.
Our test dump was about 480 KB for a small instance. Yours will be larger if you keep a lot of execution history.
Step 3: Change the version and pull
Edit one line in .env:
N8N_VERSION=2.41.4
Then pull the new image before stopping anything, so the downtime is only the restart:
docker compose pull n8n
docker compose up -d n8n
up -d recreates only the n8n container. Postgres and your data volume are untouched. On start, n8n runs its database migrations automatically. In our drill it ran 5 migrations and was serving again about 40 seconds after pull began.
Step 4: Verify before you walk away
docker compose ps
docker compose exec n8n n8n --version
docker compose logs --tail 100 n8n
Check three things:
n8n --versionprints the new version (ours printed2.41.4).- The log has no errors after "Finished migration". Deprecation notices are normal: 2.41 warns about future default changes for things like
N8N_RUNNERS_MODEandN8N_RUNNERS_TASK_TIMEOUT. Set the ones you rely on explicitly so a later release doesn't change them under you. - Your credentials still decrypt. Open a workflow that uses a credential and run it once, or run the test from the backup tutorial. In our drill, the same workflow count and the same decrypted credential value came back after the upgrade.
Also open any workflow with a Webhook trigger and confirm the production URL still points at your domain, not localhost:5678.
Step 5: If something breaks, roll back properly
The tempting rollback is to put the old version back in .env and restart. Don't rely on it. n8n migrates the database forward on upgrade and does not migrate it back when you start an older version.
In our drill, the naive rollback (2.41.4 → 2.40.6 on the already-migrated database) did start and showed the workflow. That was a small minor-version jump with additive migrations; there is no guarantee the next one will be as forgiving, and an old version running on a newer schema can fail in ways that only show up later.
The rollback that is guaranteed to be consistent is restoring the pre-upgrade backup:
docker compose stop n8n
docker compose exec -T postgres psql -U n8n -d postgres -c 'DROP DATABASE n8n WITH (FORCE);' -c 'CREATE DATABASE n8n OWNER n8n;'
docker compose exec -T postgres pg_restore -U n8n -d n8n --no-owner < n8n-before-upgrade.dump
cp env-before-upgrade.backup .env
docker compose up -d n8n
Anything that happened between the upgrade and the rollback (new executions, edited workflows) is lost, which is another reason to verify right after upgrading rather than a week later. In our drill, the restored 2.40.6 instance came back with the workflow and a credential that decrypted correctly.
Drill results (September 30, 2026)
| Step | Result |
|---|---|
| Start on 2.40.6, create a workflow + an encrypted credential | OK |
Back up: pg_dump -Fc + .env copy |
480 KB dump |
Upgrade to 2.41.4 (pull + up -d) |
5 migrations, ~40 s until ready |
| After upgrade: workflows / credential decrypts | 1 / yes |
| Naive rollback to 2.40.6 on the migrated database | Started (not recommended; see Step 5) |
| Proper rollback: restore dump + 2.40.6 | Workflows 1, credential decrypts |
Test setup: Docker Compose v2.39, postgres:17-alpine, docker.n8n.io/n8nio/n8n. (On our test machine both containers used host networking; the procedure is identical on the default Compose network.)
Common mistakes
- Upgrading without a dump because "it's just a minor version". The dump takes seconds. Restoring from nothing takes days.
- Backing up the database but not
.env. Without the originalN8N_ENCRYPTION_KEY, credentials are unrecoverable. The free n8n-key-check CLI confirms a key decrypts your credentials before you need it. - Using
latestin production. You lose control over when upgrades happen and which versions you skip. - Upgrading on Friday evening. Upgrade when you have an hour to watch the logs and run your key workflows.
Done-for-you version
The n8n Production Self-Hosting Kit turns this into scripts: upgrade.sh <version> takes a backup first, refuses latest, pulls, restarts and waits for the health check (with an optional automatic rollback), and there's an encrypted backup.sh, a restore.sh that refuses a mismatched key, and a restore drill that proves your credentials decrypt. Tested with Docker Compose, Postgres 17 and Caddy for HTTPS.
Not affiliated with n8n GmbH.
Free tool: n8n Docker Compose Generator (Postgres + HTTPS)
Written with AI assistance. Every formula, command and number on this page was tested before publishing (see each section). Product links go to our store.