The first time n8n starts, it generates a random encryption key and saves it in its data folder. Every credential you add afterwards (your Slack token, your Google sign-in, your API keys) is encrypted with that key. n8n’s backup docs say that without it, restored data and encrypted credential exports can’t be decrypted. A database backup without the key gives you your workflows back with every credential unreadable.
Most self-hosting regret comes from details like that one. The server itself is cheap: a 1 GiB DigitalOcean Droplet costs $6 a month. This checklist covers the rest, in the order you’d do it, using n8n’s own Docker Compose setup.
The short answer
- Self-host when a workflow needs to run programs on your own server (the Execute Command node is self-hosted only), or your run volume makes per-execution pricing painful.
- Start on a $6, 1 GiB Droplet with SQLite. Move to the $12, 2 GiB size if you add Postgres or process large files.
- Before the first start, generate an encryption key, store it in your password manager, and pass it in as
N8N_ENCRYPTION_KEY. - Back up the whole
.n8nfolder with n8n stopped, keep a copy off the server, and test one restore. - Skip self-hosting if you’d rather never SSH into a server. n8n Cloud Starter is €20/month billed annually, and Make’s free plan covers many small workflows.
What it costs, and when Cloud is the better buy
Prices from DigitalOcean’s and n8n’s pricing pages, checked November 9, 2026.
| Option | Monthly cost | What you get |
|---|---|---|
| DigitalOcean Basic, 1 GiB | $6 | 1 vCPU, 25 GiB SSD, 1,000 GiB transfer |
| DigitalOcean Basic, 2 GiB | $12 | 1 vCPU, 50 GiB SSD, 2,000 GiB transfer |
| Droplet backups | +20% weekly or +30% daily | $1.20 or $1.80 on the $6 Droplet |
| n8n Cloud Starter | €20, billed annually | 2,500 executions a month, 5 concurrent |
| n8n Cloud Pro | €50, billed annually | 10,000 executions a month |
So self-hosting runs about $7.20 a month with weekly backups, against €20 for Cloud Starter. The money gap is small. Your time costs more. Assume an hour a month for updates and checks once it’s set up, plus a first evening for the install. If an hour of your time is worth more than the difference, Cloud wins on cost alone.
Cloud can’t do one thing at all. The Execute Command node, which runs shell commands, “isn’t available on n8n Cloud” according to n8n’s docs. If your workflow renders video or runs a local script, you self-host. Our Make vs n8n comparison goes through the execution math in more detail.
Before you start
You need four things ready:
- A domain you control, and a subdomain for n8n (for example
n8n.yourdomain.com). - A DNS A record pointing that subdomain at the Droplet’s IP address. n8n’s guide lists this as a requirement.
- An SSH key on your computer, added to DigitalOcean when you create the Droplet.
- A password manager entry ready for the encryption key.
The checklist, step by step
1. Size the Droplet
Pick the $6 Basic Droplet (1 GiB) for SQLite and light workflows; pick $12 (2 GiB) if you plan to run Postgres alongside n8n or handle big files. n8n’s install guide gives no minimum hardware, so this sizing is our judgment from what runs on the box: n8n, the Traefik proxy, and the database. Add a 1 to 2 GiB swap file on the 1 GiB size so a memory spike slows things down instead of killing the container. Choose a Linux distribution that Docker supports, such as Ubuntu LTS.
The $4 Droplet has 512 MiB of RAM and 10 GiB of disk. Skip it for n8n.
2. Prepare the server
Lock the server down before n8n is reachable.
- Log in with your SSH key and turn off password logins for SSH.
- Turn on automatic security updates for the operating system.
- Allow only ports 22, 80, and 443 in the firewall. Traefik needs 80 and 443 to get certificates and serve HTTPS.
- Install Docker Engine and the Docker Compose plugin from Docker’s own instructions for your distribution.
The official compose file binds n8n’s port 5678 to 127.0.0.1 only, so n8n is reachable through Traefik over HTTPS and nowhere else. Keep it that way.
3. Generate the encryption key first
Create the key yourself, store it, and give it to n8n on the first start. n8n will otherwise generate one and keep it in the config file inside .n8n. Setting it yourself means a copy lives in your password manager from day one.
openssl rand -hex 32
Save the output in your password manager, then put it in the .env file in the next step. Set it before the first start. Changing the key on an instance that already holds credentials means re-entering or migrating every one of them.
4. Write the compose file and .env
Use n8n’s Docker Compose guide as the base. n8n’s single-container Docker page is marked outdated and points to the Compose guide as the recommended method. The skeleton below follows that guide (Traefik in front, Let’s Encrypt certificates, n8n behind it) with three changes we’d make: a pinned image version, the encryption key from .env, and N8N_PROXY_HOPS=1, which n8n’s webhook-URL docs set when n8n sits behind a reverse proxy. We also drop the guide’s --api.insecure=true Traefik flag, which only serves Traefik’s own dashboard.
.env:
DOMAIN_NAME=yourdomain.com
SUBDOMAIN=n8n
GENERIC_TIMEZONE=America/New_York
SSL_EMAIL=you@yourdomain.com
N8N_ENCRYPTION_KEY=paste-the-key-from-step-3
compose.yaml:
services:
traefik:
image: "traefik"
restart: always
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
- "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
- "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- traefik_data:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
n8n:
image: n8nio/n8n:<version> # pin the release you tested; see step 10
restart: always
ports:
- "127.0.0.1:5678:5678"
labels:
- traefik.enable=true
- traefik.http.routers.n8n.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
- traefik.http.routers.n8n.tls=true
- traefik.http.routers.n8n.entrypoints=web,websecure
- traefik.http.routers.n8n.tls.certresolver=mytlschallenge
# keep the security-header middleware labels from n8n's guide here
environment:
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- N8N_WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
- N8N_PROXY_HOPS=1
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- TZ=${GENERIC_TIMEZONE}
- N8N_RESTRICT_FILE_ACCESS_TO=/files
volumes:
- n8n_data:/home/node/.n8n
- ./local-files:/files
volumes:
n8n_data:
traefik_data:
One version note from n8n’s docs: N8N_WEBHOOK_URL replaces WEBHOOK_URL, which is deprecated from n8n 2.35.0; if you pin an older release, use WEBHOOK_URL instead. Get this variable right, because n8n uses it to build the webhook URLs it shows you, and a wrong value gives you URLs that point nowhere.
Lock the .env file down with chmod 600 .env. It now holds the key to every credential.
5. Start it and claim the owner account
Start the stack, then open the URL immediately and create the owner account.
docker compose up -d
docker compose logs -f n8n
When the logs settle, open https://n8n.yourdomain.com. The first visitor sets up the owner account, so do it within minutes of starting, with a long unique password from your password manager. If the certificate doesn’t appear, check that the A record resolves to the Droplet and that ports 80 and 443 are open.
6. Choose the database
Stay on SQLite unless you have a reason to change. n8n uses SQLite by default and also supports PostgreSQL. The database docs we checked don’t push one over the other for a single instance. MySQL and MariaDB support has been deprecated since n8n 1.0, so don’t start there.
Postgres makes sense if you already run it elsewhere, or if execution history grows into gigabytes. It also means a second container, more memory (hence the 2 GiB Droplet), and a second thing to back up with Postgres’s own tools. For a solo builder with a dozen workflows, SQLite inside the .n8n folder keeps backups to one folder.
7. Set up backups, and test a restore once
Back up the whole .n8n folder (it holds the database and the config file with the key), keep one copy off the server, and practise a restore. n8n’s backup and restore page is clear on three points:
- A complete backup is the
.n8nfolder plus your Postgres database if you use one. - With SQLite, “stop n8n before copying the
.n8nfolder,” because copying while n8n writes “can produce an inconsistent backup.” - Restore in this order: stop n8n, restore
.n8n, restore Postgres if used, restore your config includingN8N_ENCRYPTION_KEY, start n8n.
A nightly backup script for the compose setup above (run docker volume ls to confirm the volume name, which Compose prefixes with your project folder name):
#!/bin/sh
set -e
cd /opt/n8n
docker compose stop n8n
docker run --rm -v n8n_n8n_data:/data -v /opt/n8n/backups:/backup alpine \
tar czf /backup/n8n-data-$(date +%F).tar.gz -C /data .
docker compose start n8n
# then copy /opt/n8n/backups off the server and delete archives older than 30 days
This stops n8n for a few seconds, so schedule it for a time when no workflow runs. For a second, readable layer, n8n’s CLI exports workflows and credentials as separate JSON files:
docker compose exec -T -u node n8n n8n export:workflow --backup --output=/files/backups/latest/
docker compose exec -T -u node n8n n8n export:credentials --backup --output=/files/backups/latest/
The CLI export leaves out users, execution history, variables, and instance settings, so it can’t replace the folder backup. Credentials in it stay encrypted with your key. Avoid --decrypted exports unless you are migrating; n8n warns that all sensitive information is visible in those files.
DigitalOcean’s weekly Droplet backups (20% of the Droplet price) are a useful extra layer, and they live in the same account as the server. Keep your own copy somewhere else too.
Test the restore once. Create a second $6 Droplet, restore the archive and .env, start it, and open one credential. If it decrypts, your backup works. Destroy the test Droplet afterwards so it stops billing.
8. Keep execution data from filling the disk
Leave pruning on, and turn off saving successful runs if you don’t read them. n8n prunes by default: finished executions older than 336 hours (14 days) are deleted, and once there are more than 10,000, the oldest go first. Set EXECUTIONS_DATA_SAVE_ON_SUCCESS=none to stop storing successful runs at all; failed runs still get saved for debugging.
On SQLite, pruned space “isn’t automatically freed up.” The file stays the same size and reuses the space. To shrink it, set DB_SQLITE_VACUUM_ON_STARTUP=true for one restart, knowing n8n warns it’s a long, blocking operation. Check disk use monthly with df -h.
9. Add an error workflow and an uptime check
Two alerts, set up the same day you go live.
- Error workflow. Create a workflow that starts with the Error Trigger node and sends you a message (email, Slack, Telegram). Then in every production workflow open Settings and choose it under Error workflow. It receives the failing workflow’s name, the node, and the error.
- Uptime check. Point a free external uptime monitor at
https://n8n.yourdomain.com/healthz. n8n’s monitoring docs say it returns 200 when the instance is reachable./healthz/readinessreturns 200 only once the database is connected and migrated, which makes it the stricter check. Neither needs extra configuration on a single instance.
The error workflow can’t tell you the server is down, because it runs on the same server. That’s what the outside check is for.
10. Update without breaking things
Pin a version, back up, then pull. n8n’s docs recommend “taking a full backup before updating,” and give the Compose update sequence:
# 1. run the backup script from step 7
# 2. change the image tag in compose.yaml to the new version
docker compose pull
docker compose down
docker compose up -d
Read the release notes before every update, and before a major version in particular. Version 2.0 changed defaults, for example by disabling Execute Command. With a pinned tag, the version changes only when you edit compose.yaml. If the new version misbehaves, put the old tag back and restore the backup you took a minute earlier.
Update the operating system and Docker on the same schedule. Once a month is a reasonable rhythm for a solo instance.
11. Enable Execute Command only if a workflow needs it
Leave it off unless a specific workflow requires it. n8n disables the Execute Command node by default from version 2.0 and warns it “can introduce significant security risks” where untrusted users have access. With Docker, the command runs inside the n8n container, so any tool your workflow calls (ffmpeg, a script, a headless browser) has to be installed in a custom image built on top of n8n’s.
To turn it on, set the NODES_EXCLUDE variable so it no longer lists n8n-nodes-base.executeCommand. n8n’s docs show NODES_EXCLUDE: "[]" to clear the list, but that unblocks every node blocked by default, so check what else you’d be enabling. Our short-form video pipeline is an example of a workflow that needs this node.
The license line you can’t cross
The Community edition is under n8n’s Sustainable Use License. It allows use “only for your own internal business purposes or for non-commercial or personal use,” and you may provide the software to others “only if you do so free of charge for non-commercial purposes.” Running your own site’s automations on it is fine. Charging clients for a hosted n8n you run for them is outside the license. Files with .ee. in their names belong to a separate enterprise license, so paid features stay off.
The copyable checklist
[ ] Domain + A record for n8n subdomain pointing at the Droplet
[ ] Droplet: $6 / 1 GiB (SQLite) or $12 / 2 GiB (Postgres, big files); swap on 1 GiB
[ ] SSH key login only; automatic OS security updates; firewall 22/80/443
[ ] Docker Engine + Compose plugin installed from Docker's docs
[ ] Encryption key generated (openssl rand -hex 32) and saved in password manager
[ ] .env written, chmod 600; image tag pinned in compose.yaml
[ ] N8N_HOST, N8N_PROTOCOL=https, N8N_WEBHOOK_URL (WEBHOOK_URL before 2.35), N8N_PROXY_HOPS=1
[ ] Port 5678 bound to 127.0.0.1 only
[ ] Started; owner account created right away with a long unique password
[ ] Nightly backup of the .n8n volume with n8n stopped; copy kept off the server
[ ] CLI export of workflows + credentials as a second layer
[ ] One test restore on a throwaway Droplet; a credential decrypts
[ ] Pruning on (default 14 days / 10,000); EXECUTIONS_DATA_SAVE_ON_SUCCESS=none if unused
[ ] Error workflow (Error Trigger) set on every production workflow
[ ] External uptime check on /healthz or /healthz/readiness
[ ] Monthly: read release notes, back up, bump tag, pull, restart; OS updates; df -h
[ ] Execute Command left disabled unless a workflow needs it
Who this is for, and who should skip it
Self-host if you’re comfortable on a Linux command line, you need Execute Command or local programs, or you run enough executions that Cloud’s tiers would push you to Pro or beyond.
Skip it if your automations are a handful of small scheduled jobs. Make Free covers many of them, and a simple fetch-and-notify job can run as a script on GitHub Actions for free, as we compare in do you still need Make or n8n if AI can write the script. Skip it too if an outage on a weekend would hurt you and you won’t be there to fix it.
Quick answers
Is a $6 Droplet enough for n8n? For n8n with SQLite and light workflows, 1 GiB with a swap file is a reasonable start; n8n’s guide states no minimum. Go to 2 GiB for Postgres, large files, or many runs at once.
What happens if I lose the encryption key? Your workflows restore, but saved credentials can’t be decrypted, according to n8n’s backup docs. You’d re-enter every API key and reconnect every OAuth account by hand.
Can I move from self-hosted n8n to n8n Cloud later? Workflows export as JSON from the CLI and import into another instance. Credentials encrypted with your key won’t decrypt elsewhere, so plan to re-enter them on Cloud.
Start at the Automation Workflows pillar for the full map. Still choosing a platform? Read Make vs n8n for solo builders, or check whether a script on a free scheduler covers your job in Make or n8n vs an AI-written script.