The one thing that matters most: find your encryption key
Every credential n8n stores (API keys, OAuth tokens, database passwords) is encrypted with one encryption key. Move to a new machine with a different key and none of the credentials can be decrypted; you have to re-enter every one.
The key lives in one of two places:
- The
N8N_ENCRYPTION_KEYenvironment variable: check your Zeabur service’s Variables tab - The
configfile in the n8n folder: if the variable isn’t set, n8n generates a key on first start and writes it here. In the official image the path is/home/node/.n8n/config, and it looks like this:
{
"encryptionKey": "a random string"
}
Copy the key into your password manager.
A trap we found in testing: importing with the wrong key raises no error.
We exported credentials with KEY_A and imported them into an n8n configured with KEY_B. It printed
Successfully imported 1 credential.and everything looked fine. Only when the credential was used did this appear:
Credentials could not be decrypted. The likely reason is that a different "encryptionKey" was used to encrypt the data.So always do the verification in Step 4 below. Don’t trust the import success message.
Step 1: Export on Zeabur
According to Zeabur’s docs, the Overview tab of a service has a Command section for running commands inside the container. Export the workflows and credentials into the n8n folder:
n8n export:workflow --all --output=/home/node/.n8n/workflows.json
n8n export:credentials --all --output=/home/node/.n8n/credentials.json
The Files section on the same tab lets you browse and download files in the container. Download these two files and config.
The exported credentials.json is encrypted; you can’t read the passwords in it. In our test, the SECRET123 value we had set could not be found anywhere in the file. That is why it only works together with the original key.
Alternative: back up the whole volume. If your n8n uses the built-in SQLite (the folder contains database.sqlite), the offline backup on Zeabur’s Backup tab can download the whole folder. According to Zeabur’s docs you must suspend the service first (Settings → Suspend Service), and that’s right: a running SQLite has a -wal file, and copying without stopping can produce incomplete data.
Step 2: Set up the new n8n on a VPS
Create an Ubuntu VPS, install Docker, point your domain’s A record (for example n8n.example.com) at the VPS IP, and create these two files:
# docker-compose.yml
services:
n8n:
image: docker.n8n.io/n8nio/n8n
restart: unless-stopped
environment:
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY} # reuse the key from Zeabur
- N8N_HOST=n8n.example.com
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://n8n.example.com/
volumes:
- n8n_data:/home/node/.n8n
caddy:
image: caddy:2
restart: unless-stopped
ports: ["80:80", "443:443"]
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
volumes:
n8n_data:
caddy_data:
# Caddyfile
n8n.example.com {
reverse_proxy n8n:5678
}
echo "N8N_ENCRYPTION_KEY=the key copied from Zeabur" > .env
docker compose up -d
Caddy requests the HTTPS certificate automatically, provided DNS already points at this VPS and ports 80 and 443 are open. When n8n runs behind a reverse proxy, n8n’s docs require WEBHOOK_URL so the webhook URLs it generates are the correct public ones.
Step 3: Import
docker compose cp workflows.json n8n:/tmp/
docker compose cp credentials.json n8n:/tmp/
docker compose exec n8n n8n import:workflow --input=/tmp/workflows.json
docker compose exec n8n n8n import:credentials --input=/tmp/credentials.json
Step 4: Verify the credentials really decrypt
docker compose exec n8n n8n export:credentials --all --decrypted --output=/tmp/check.json
docker compose exec n8n cat /tmp/check.json
- You can read the plain values: the key is correct
- You see
Credentials could not be decrypted: wrong key; go back to the first section
Delete the file immediately afterwards; it contains plain-text passwords:
docker compose exec n8n rm /tmp/check.json /tmp/credentials.json
Step 5: Wrap up
- Open each workflow and check its trigger; for webhook-triggered ones, update the URL in the other service (LINE, Stripe, form tools)
- Given Zeabur’s security incidents, we recommend replacing every third-party API key stored in your credentials (see the security post)
- Set up daily backups: at minimum, archive the
n8n_datavolume every day and store it somewhere else
For multi-service tools such as Dify or LobeChat, use the docker-compose.yml from their official repositories and add Caddy the same way. For databases, see the database post.