n8n connects your tools: a form that creates a customer record, an order that lands in the accounting system, an alert that shows up in a chat channel. The plans n8n hosts for you are priced by executions per month (2,500 on the smallest one, according to its pricing page in November 2026). The Community edition counts nothing: on your own server, the only cap is the machine.
We measured that cap in Montréal: our n8n image, a manual install with Docker Compose, a real workflow (webhook, processing, HTTP request) and thousands of calls. Then backups, updates and the license.
1. Which plan to pick
n8n runs as a single Node.js process. At rest, after an hour of work, its container used 373 MB of memory and Caddy about 20 MB. During our load tests, n8n never went above 1,023 MB. A Nano (1 vCPU, 2 GB) is enough to start with: we saw no crash.
| Plan | Resources | For n8n | Per month |
|---|---|---|---|
| Nano | 1 vCPU · 2 GB · 20 GB | personal use or a small team, a few dozen workflows, about 5 executions per second at most | 8.50 CAD |
| Starter | 2 vCPU · 4 GB · 40 GB | more memory headroom and a second core for the system and Caddy; we did not measure it | 16 CAD |
| Pro | 4 vCPU · 8 GB · 80 GB | workflows that handle files or large data sets in memory | 30 CAD |
The CPU runs out before the memory does (section 5). We did not test workflows that handle files (PDFs, images, large CSV exports): for those, plan for more memory. To try it out, take an hourly Nano (0.018 CAD an hour) and destroy it afterwards.
2. Deploy with our n8n image
The catalog has an n8n image, available from the Nano up. It is Ubuntu 24.04
with Docker, with n8n running behind Caddy. Every VM gets its own domain name (shaped
like vm-xxxxxxxx.ffxf.zone): on first boot, Caddy gets a Let's Encrypt
certificate for it, and n8n answers over HTTPS at
https://your-vm-domain. In our test, the certificate was in place less than
two minutes after the order. Everything lives in /opt/n8n:
compose.yml, .env and Caddyfile. The SSH user is
ubuntu.
Two things to do right away. First, create the owner account immediately: whoever fills in the welcome screen first becomes the owner of the instance. Certificates are published in public logs, and a scanning bot hit our VM one second after its certificate was issued.
Then update n8n. The image ships the version pulled when it was built: 2.35.3 in
our test, while the stable release that day was 2.46.4. We have rebuilt it since, but the
gap comes back with every new n8n release. The compose file
follows the latest tag, so pulling the new image is enough:
cd /opt/n8n
sudo docker compose pull
sudo docker compose up -d
sudo docker compose exec n8n n8n --version
To use your own domain, point its A and AAAA records at the machine, replace the VM name
in the Caddyfile and in WEBHOOK_URL (in .env),
then run sudo docker compose up -d again.
3. Install by hand with Docker Compose
A manual install lets you pick the version and set the encryption key yourself. We did
it on a Nano with the catalog's Docker image (see
Docker on a VPS to start from a bare Ubuntu).
Replace DOMAIN with your VM's name or your own domain:
sudo mkdir -p /opt/n8n && cd /opt/n8n
DOMAIN=vm-xxxxxxxx.ffxf.zone
printf 'DOMAIN=%s\nN8N_ENCRYPTION_KEY=%s\n' "$DOMAIN" "$(openssl rand -hex 32)" | sudo tee .env >/dev/null
sudo chmod 600 .env
printf '%s {\n\treverse_proxy n8n:5678\n}\n' "$DOMAIN" | sudo tee Caddyfile
Then compose.yml:
services:
n8n:
image: docker.n8n.io/n8nio/n8n:2.46.4
restart: unless-stopped
environment:
- N8N_HOST=${DOMAIN}
- N8N_PROTOCOL=https
- N8N_PROXY_HOPS=1
- WEBHOOK_URL=https://${DOMAIN}/
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- GENERIC_TIMEZONE=America/Toronto
- TZ=America/Toronto
- EXECUTIONS_DATA_MAX_AGE=168
- EXECUTIONS_DATA_PRUNE_MAX_COUNT=20000
volumes:
- n8n_data:/home/node/.n8n
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
volumes:
n8n_data:
caddy_data:
sudo docker compose up -d
Download and first start took 1 min 10 s on the Nano. A few settings are worth explaining:
WEBHOOK_URL: without it, n8n shows webhook addresses onhttp://localhost:5678, which nobody outside can reach.N8N_PROXY_HOPS=1: tells n8n a reverse proxy (Caddy) sits in front of it, so it reads the real client address.GENERIC_TIMEZONE: the time zone for scheduled triggers. Without it, a workflow set for "every day at 8 AM" runs on New York time, n8n's default.EXECUTIONS_DATA_MAX_AGEandEXECUTIONS_DATA_PRUNE_MAX_COUNT: by default n8n keeps execution history for 14 days and 10,000 entries; here, 7 days and 20,000. With SQLite, freed space is reused but not returned to the disk.
Caddy gets the certificate on its own if ports 80 and 443 are open. With Nginx (see our
Nginx and HTTPS guide), pass the
Upgrade and Connection headers: the editor uses a WebSocket
connection.
4. A test workflow: webhook, processing, HTTP request
To measure something realistic, we built a four-node workflow:
- Webhook: receives a
POSTon/webhook/commandeand replies with the last node's output; - Calcul (Code node): adds up the order lines and cleans the customer's address;
- Envoi (HTTP Request node): sends the result as JSON to an HTTPS receiver on another VM;
- Reponse (Code node): returns the computed data and confirms the delivery.
The Calcul node's code:
const b = $input.first().json.body;
const lignes = b.lignes || [];
const total = lignes.reduce((s, l) => s + l.qte * l.prix, 0);
return [{ json: {
commande: b.id,
client: String(b.client || '').trim().toLowerCase(),
nb_lignes: lignes.length,
total: Math.round(total * 100) / 100,
recu_le: new Date().toISOString(),
} }];
Once the workflow is published, one call checks it:
curl -s -X POST https://vm-xxxxxxxx.ffxf.zone/webhook/commande \
-H 'Content-Type: application/json' \
-d '{"id":"C-1042","client":" Marie@Exemple.ca ","lignes":[{"qte":2,"prix":12.5},{"qte":1,"prix":30}]}'
{"commande":"C-1042","client":"marie@exemple.ca","nb_lignes":2,"total":55,"recu_le":"2026-11-03T15:32:04.199Z","transmis":true}
The full round trip, outbound HTTPS call included, had a 0.16 s median over 100 calls sent one at a time.
5. At rest and under 1,000 calls
Calls came from another VM in the same data centre, using hey; memory and
CPU were sampled every second on the n8n machine (docker stats and
vmstat).
hey -n 1000 -c 10 -m POST -T application/json -D body.json \
https://vm-xxxxxxxx.ffxf.zone/webhook/commande
| Test (Nano, 1 vCPU) | Throughput | Median | p95 | p99 | Peak n8n memory |
|---|---|---|---|---|---|
| At rest | – | – | – | – | 373 to 466 MB, CPU under 1% |
| 100 calls, one at a time | 5.6/s | 0.16 s | – | 0.48 s | – |
| 1,000 calls, 10 concurrent (2.35.3) | 5.0/s | 1.73 s | 3.17 s | 8.57 s | 766 MB |
| 1,000 calls, 50 concurrent (2.35.3) | 5.1/s | 9.51 s | 11.34 s | 11.55 s | 1,023 MB |
| 1,000 calls, 10 concurrent (2.46.4) | 5.7/s | 1.72 s | 2.29 s | 3.02 s | 744 MB |
All 3,100 calls got a 200 response, with no errors and no restarts. But throughput tops out around 5 executions per second: under load, the vCPU was over 80% busy, almost entirely with n8n's main process. Sending more calls at once does not go faster, it only makes the queue longer: at 50 concurrent, each call waited 9 to 12 seconds.
A workflow reduced to a webhook that replies right away tops out at 6.8 executions per
second; webhook plus Code node without the HTTP request, at 8.5 (300 calls, 10
concurrent). Turning off saving of successful executions
(EXECUTIONS_DATA_SAVE_ON_SUCCESS=none) made no measurable difference. Most
of the cost is n8n itself, on every execution, whatever the workflow.
At 5 per second, a Nano can in theory handle 18,000 executions an hour, far more than most small-business automations need. It is a poor fit, though, for a public webhook that receives bursts: calls pile up and an impatient sender will eventually give up. We could not measure the Starter during this test. Since the main process does nearly all the work, do not count on a second vCPU doubling throughput until you have checked. The scaling path n8n documents is queue mode, included in the Community edition, with separate workers.
On disk, the data folder weighed 53 MB after about 3,000 saved executions, event logs included.
6. Backups and the encryption key
All of n8n's state lives in the volume mounted on /home/node/.n8n: the
SQLite database (workflows, users, history) and the config file that holds
the encryption key. n8n encrypts your credentials (API tokens, passwords) with that key
before writing them to the database. A database without its key is useless for
credentials.
Our image does not set N8N_ENCRYPTION_KEY: n8n generated the key on first
start and stored it in config, inside the volume. With the manual install
above, the key is also in /opt/n8n/.env. Keep a copy somewhere other than
the server, a password manager for example.
n8n's documentation says to stop n8n before copying the folder when using SQLite. On the Nano, stopping, archiving and restarting took 5.7 s:
cd /opt/n8n
sudo docker compose stop n8n
sudo docker run --rm -v n8n_n8n_data:/data:ro -v /opt/n8n:/backup alpine \
tar czf /backup/n8n-$(date +%F).tar.gz -C /data .
sudo docker compose start n8n
Then copy the archive (3.3 MB in our case) and the .env file off the
machine. We restored that archive on the other VM: the workflow was published, the
webhook answered and the test credential could be read. To restore:
sudo docker compose stop n8n
sudo docker run --rm -v n8n_n8n_data:/data -v /path/to/archive:/backup alpine \
sh -c "rm -rf /data/* && tar xzf /backup/n8n-YYYY-MM-DD.tar.gz -C /data"
sudo docker compose start n8n
The trap: if N8N_ENCRYPTION_KEY is set and does not match the key in the
restored config file, n8n refuses to start and restarts in a loop with this
message:
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.
The n8n export:workflow --backup and
n8n export:credentials --backup exports are for moving workflows, but they
leave out users, history and the key: they do not replace a copy of the volume. Machine
backups in the console add a second copy (7 manual ones included, automatic ones at
3 CAD a month).
7. Updates
n8n ships a minor release almost every week. Its documentation recommends updating at
least once a month, reading the
release notes
and taking a full backup first. With the version pinned in compose.yml:
cd /opt/n8n
sudo sed -i 's|n8nio/n8n:2.46.4|n8nio/n8n:NEW_VERSION|' compose.yml
sudo docker compose pull
sudo docker compose up -d
Our move from 2.35.3 to 2.46.4 ran the database migrations on its own. The container
answered /healthz 13 seconds after the restart, but webhooks returned 404
for up to 25 more seconds, while n8n reactivated the published workflows. Plan
for that gap, or update at a quiet hour.
On November 3, 2026, stable pointed to 2.46.4, but a 3.1.0 was already
published. Before a major version jump, try it on a copy: an hourly VM restored from
your archive.
8. License and limits
n8n is not open source by the Open Source Initiative's definition. It is published under the Sustainable Use License, which n8n files under the "fair-code" model: source available, free to use, with commercial use restricted. According to the license FAQ:
- allowed: automating your own business, personal projects, building workflows for clients on your instance as long as they cannot create or edit them, charging for building, setting up and maintaining workflows;
- not allowed without a commercial agreement: hosting n8n as a service where your clients build their own workflows, letting them configure the logic through your UI, the API or an AI agent, reselling n8n under your own brand.
The Community edition includes almost everything. According to the documentation, it lacks: projects and sharing workflows and credentials between users, single sign-on (SAML, LDAP), environments and Git version control, external secrets, custom variables, external storage for binary data, log streaming and multi-main mode. A free registration by email unlocks folders, debugging in the editor and custom execution data.
Finally, SQLite is fine for a single instance; queue mode, with several workers, needs PostgreSQL and Redis. Running the server is your job (see our guide to securing Ubuntu). The other images in the catalog are on the Images page.