Skip to content
Guides

Switch your n8n to Docker before v3, or you can't upgrade

n8n 3.0 drops npm installs. How to move an npm or npx n8n instance to Docker with your workflows, credentials and encryption key intact, tested on 2.45.7.

By then8nblog Editors6 min read
Tested on n8n 2.45.7intermediate
Illustration of moving n8n from npm to Docker before 3.0: copy the ~/.n8n folder with the database, WAL and config files, keep the encryption key, run the same version first, tested on n8n 2.45.7 with webhooks and logins kept

To move n8n from npm to Docker, stop n8n, copy your ~/.n8n folder (the SQLite database and the config file with your encryption key) into a Docker volume mounted at /home/node/.n8n, give it to user ID 1000, and start the official image at the same version you ran with npm. Your workflows, credentials and logins come along. Do it now on 2.x: n8n 3.0 no longer supports npm or npx n8n installs and doesn't publish a runnable package to npm.

We tested this on November 1, 2026: we installed n8n 2.45.7 with npm (in a Node.js 24 container standing in for a server), created an owner, a credential and a published webhook workflow, moved the data to the n8nio/n8n:2.45.7 image with the Compose file and commands below, and checked that everything still worked. We also reproduced the two errors you get when the encryption key doesn't come along, and their fix.

The short version

  • Why now: per n8n's 3.0 breaking changes, "n8n 3.0 no longer supports installations run using npm or npx n8n." Move first, then upgrade.
  • What to move: the whole ~/.n8n folder, including database.sqlite, its -wal and -shm files, and config, which holds the encryption key.
  • Same version first: run the Docker image at the version npm ran (n8n --version), then upgrade in a separate step.
  • Permissions: the image runs as user node, UID 1000, so the copied files must belong to UID 1000.
  • If you forget the key: n8n generates a new one and exits with Deployment key 'signing.hmac' cannot be read with this instance encryption key. Pass the old key as N8N_ENCRYPTION_KEY and delete the newly created config file.

Before you start

  1. Find your version. Run n8n --version. In our test, the npm install reported 2.45.7.
  2. Find your data folder. By default it's ~/.n8n in the home folder of the user that runs n8n. If you set N8N_USER_FOLDER, n8n creates the .n8n folder inside that path instead, per n8n's deployment variables.
  3. Collect your environment variables. Anything you set in a systemd unit, a PM2 file, a .env file or your shell profile (WEBHOOK_URL, GENERIC_TIMEZONE, DB_TYPE and the other DB_ variables if you use Postgres, and so on) needs to go into the Docker setup too.
  4. Stop n8n (systemctl stop, pm2 stop, or Ctrl+C), so nothing writes to the database while you copy it.
  5. Back up the folder, for example tar czf n8n-backup.tgz -C ~ .n8n.

The encryption key is the part people lose. n8n's encryption key docs explain that n8n generates a random key on first launch, saves it in the ~/.n8n folder, and uses it to encrypt credentials before they go into the database. Without it, your stored credentials can't be decrypted. Ours looked like this:

cat ~/.n8n/config
# {
#   "encryptionKey": "<your-key>"
# }

Copy that value somewhere safe, such as a password manager, before you go further.

Step 1: Write a Docker Compose file

Create a folder for the new setup with a compose.yml and an .env. This runs n8n with an external task runner, the setup n8n's task runner docs recommend for production:

services:
  n8n:
    image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      - N8N_RUNNERS_MODE=external
      - N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
      - N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0
      # add the variables you collected, for example:
      # - GENERIC_TIMEZONE=Europe/Berlin
      # - WEBHOOK_URL=https://<your-domain>/
    volumes:
      - n8n_data:/home/node/.n8n

  task-runners:
    image: n8nio/runners:${N8N_VERSION}
    restart: unless-stopped
    environment:
      - N8N_RUNNERS_TASK_BROKER_URI=http://n8n:5679
      - N8N_RUNNERS_AUTH_TOKEN=${RUNNERS_AUTH_TOKEN}
    depends_on:
      - n8n

volumes:
  n8n_data:
echo "N8N_VERSION=2.45.7" > .env     # the version `n8n --version` printed
echo "RUNNERS_AUTH_TOKEN=$(openssl rand -hex 32)" >> .env

Use the version you actually run, not a newer one. Both images must have the same tag. For a production setup with Postgres and HTTPS, see our Docker Compose guide.

Step 2: Copy your data into the volume

Create the volume, copy the folder in, and hand it to UID 1000, the node user the image runs as (id inside the image prints uid=1000(node) gid=1000(node)):

docker compose create            # creates the n8n_data volume without starting n8n
docker run --rm \
  -v ~/.n8n:/from:ro \
  -v "$(basename "$PWD")_n8n_data":/to \
  alpine sh -c 'cp -a /from/. /to/ && chown -R 1000:1000 /to && ls -ln /to'

Docker Compose prefixes volume names with the project name, which defaults to the folder name, hence $(basename "$PWD")_n8n_data. Check the name with docker volume ls if yours differs.

The listing should show config, database.sqlite, and usually database.sqlite-wal and database.sqlite-shm, all owned by 1000 1000. Copy all of them: in our test, the -wal file still held about 4 MB after n8n had stopped. Leaving it behind can lose recent changes.

Step 3: Start n8n and check everything

docker compose up -d
docker compose logs -f n8n      # wait for "Editor is now accessible via"

Then check:

  • You can log in with your existing owner account. In our test, the same email and password worked.
  • Webhooks still fire. Our published webhook workflow returned the same response from Docker as from npm, with no republishing needed.
  • Credentials decrypt. Open a credential and test it, or run a workflow that uses one. We confirmed ours with docker compose exec n8n n8n export:credentials --id=<credential-id> --decrypted, which printed the original value. That command writes secrets in plain text, so only use it on test data.

Once everything works, disable the old service (systemctl disable n8n, pm2 delete n8n) so it can't start again against the old folder, and keep the backup until you've upgraded.

Common errors and fixes

Deployment key 'signing.hmac' cannot be read with this instance encryption key

This is what we got when we copied only database.sqlite* without the config file. n8n logged No encryption key found - Auto-generating and saving to: /home/node/.n8n/config and then exited with:

Error: Deployment key 'signing.hmac' cannot be read with this instance encryption key

The new random key can't read data encrypted with the old one. Fix it in either of two ways:

  • Copy the original config file into the volume (and chown 1000:1000 it), or
  • Delete the generated config from the volume and pass the old key as an environment variable: N8N_ENCRYPTION_KEY=<your-old-key>.

Mismatching encryption keys

If you set N8N_ENCRYPTION_KEY while a config file with a different key is in the volume, n8n refuses to start:

Error: Mismatching encryption keys. The encryption key in the settings file /home/node/.n8n/config does not match the N8N_ENCRYPTION_KEY env var. Please make sure both keys match.

We hit this after the auto-generated key from the first error had been saved. Removing that config file and keeping only the environment variable fixed it, and the credential decrypted correctly.

n8n's address '::' is not available

On a host without IPv6, n8n may stop with n8n's address '::' is not available, as it did on our test machine. Set N8N_LISTEN_ADDRESS=0.0.0.0 in the n8n service's environment.

Permission errors on startup

If the files in the volume belong to root or another user, the node user in the container can't write to them. Run the chown -R 1000:1000 step again.

Alternative: export and import with the CLI

If you'd rather start with a clean data folder, n8n's CLI can move workflows and credentials as files. On the npm install:

n8n export:workflow --backup --output=backups/workflows/
n8n export:credentials --backup --output=backups/credentials/

Then copy the backups folder to the new setup and run n8n import:workflow --separate --input=... and n8n import:credentials --separate --input=... inside the container. Exported credentials stay encrypted, so the new instance still needs the old encryption key; n8n's docs describe --decrypted exports for moving to an instance with a different key, but that file holds every secret in plain text. This route doesn't carry over users, execution history or settings, so for a straight move, copying the folder is simpler.

On patched 2.x versions, importing a workflow that contains a Function, Function Item or LangChain Code node fails, and in our test the whole import batch was rejected. Replace those nodes with the Code node first; our Migration Report guide shows how.

What about the one-line setup and Postgres?

  • The one-line setup (curl -fsSL https://get.n8n.io | sh) is what n8n's 3.0 page recommends for moving to Docker, but its own docs say it's "meant for fresh installs, not for changing an existing setup." It creates a new instance with its own data, so to keep your existing workflows and credentials, use the steps above.
  • If your npm install used Postgres, the database stays where it is. Move your DB_ variables and the encryption key (from ~/.n8n/config or N8N_ENCRYPTION_KEY) into the Docker setup, and make sure the container can reach the database host.

After the move: upgrade to 3.0

With n8n on Docker, update to the latest 2.x first (pin the new version in .env and run docker compose pull && docker compose up -d), then work through Settings > Migration Report before you try 3.0. Our guides on updating self-hosted n8n safely and the n8n 3.0 breaking changes cover the next steps.

One side note from our test: n8n's npm install docs say n8n needs Node.js "between 20.19 and 24.x," but the n8n 2.45.7 package on npm declares node >=24.0.0. If an npm upgrade complains about your Node.js version, that's why, and it's one more reason to move to Docker, where the image brings its own runtime.

Sources

  1. n8n 3.0 breaking changes, Docker-based deployment required (n8n docs)
  2. Install with npm (n8n docs)
  3. One-line setup (n8n docs)
  4. Set a custom encryption key (n8n docs)
  5. Deployment environment variables, N8N_USER_FOLDER (n8n docs)
  6. Use the command line, export and import (n8n docs)
  7. Set up task runners (n8n docs)
  8. n8n package versions and engines (npm)

then8nblog Editors

Editorial team

The then8nblog editorial team researches every article from n8n's release notes, documentation and source code, states the n8n version each guide was tested on, and has every post reviewed by a human editor before it goes live.

News

n8n 2.46: what's new and should you upgrade

What's new in n8n 2.46: personal credentials in team projects, a better 3.0 Migration Report, Edit Image v2, and which version to run in production.

6 min read