Should You Host n8n with Kamal? Setup and Tradeoffs
A practical look at hosting n8n with Kamal: deployment setup, the Caddy reverse proxy, encryption key handling, and when Docker Compose or Coolify will serve you better.
Updated May 2026. Rewritten to reflect Kamal 2.x, the way we actually deploy n8n with it, and where it stops being the right tool.
Kamal is a deployment tool from the Basecamp team that wraps Docker, SSHKit, and a small reverse proxy into a single workflow. It is good. It is also not the obvious choice for n8n. We get asked about it often enough that we wanted to write down what we have learned from running it in production for the small subset of clients where it is the right call.
At Osher Digital, we are a Brisbane-based AI and automation consultancy. We host n8n for clients on Docker Compose, Coolify, Fly.io, Railway, Render, and a few others depending on the workload. Kamal sits in a narrow middle ground that is worth understanding before you commit to it.
This guide is for engineers and ops leads who already know Docker, want to understand the Kamal angle on n8n, and want a straight answer on whether it beats their current setup. If you want a broader view first, our guide to self-hosting n8n covers the alternatives. If you have not committed to self-hosting yet, the self-host vs cloud comparison is the better starting point.
Why Kamal for n8n (and Why Most Teams Skip It)
Kamal solves a specific problem. You have a Docker image. You have one or more Linux servers you control. You want zero-downtime deploys, automatic SSL, basic health checks, and a single command to push a new version. You do not want Kubernetes, you do not want a managed PaaS, and you do not want to write Ansible playbooks.
For a Rails app or a custom Node service, Kamal is a fast win. For n8n the case is weaker. n8n already ships as a Docker image. The official Docker Compose file is short, well-tested, and handled by anyone who has ever started a container. Coolify wraps that compose file in a UI and gives you most of what Kamal gives you for free.
So when does Kamal earn its place? Three situations:
- You are already running Kamal for other services and you want one consistent deploy pipeline. Adding n8n is half a day of work and the operational simplicity is real.
- You need multi-server n8n. Kamal will deploy to a list of hosts, manage rolling restarts, and handle the proxy on each box. Coolify can do this too but Kamal is more explicit about it.
- You want a deploy config that lives in your repo and is fully reviewable in pull requests.
config/deploy.ymlis one file. That auditability is genuinely useful for regulated workloads.
If none of those apply, you probably do not need Kamal. We have moved several clients off it onto Coolify or back to Docker Compose. The Kamal benefit becomes overhead when you are deploying once a quarter and the entire team has forgotten how it works.
Prerequisites: What Kamal Actually Needs
Before you start, get the list right. The most common deployment failure we see is a half-configured server that fails partway through kamal setup.
You need a Linux server that can run Docker. Ubuntu 22.04 LTS or 24.04 LTS is what we use. 2 GB RAM is the floor for a quiet n8n instance with the default SQLite. 4 GB is what we recommend the moment you add Postgres on the same host. Disk: 40 GB SSD is fine for most teams; expect to grow if you store binary data inside workflow runs.
On your local machine you need Ruby 3.2 or later, Docker Desktop or Colima for image builds, an SSH key already deployed to the server’s root or a sudoer, and a Docker registry account. Docker Hub free tier works. We use private registries on the cloud provider for client work because public n8n configs leak environment variable names that we do not need broadcasting.
gem install kamal -v "~> 2.0"
kamal version
# 2.x.x
Kamal 1.x worked. Kamal 2.x is what you should be using in 2026. The proxy moved from Traefik to a custom kamal-proxy built on the Go standard library, which has been the single biggest reliability win in the tool. If you are still on a 1.x deploy, plan a migration; the proxy upgrade alone is worth it.
The Kamal Configuration That Works for n8n
Here is the real config/deploy.yml we use as our starting template. It assumes a single host with Postgres on the same box, which covers the majority of small to mid-size n8n deployments.
service: n8n
image: your-registry/n8n
servers:
web:
hosts:
- 203.0.113.10
options:
memory: 2g
proxy:
ssl: true
host: n8n.yourcompany.com
app_port: 5678
healthcheck:
path: /healthz
interval: 10
timeout: 5
registry:
server: registry.digitalocean.com
username: kamal
password:
- KAMAL_REGISTRY_PASSWORD
builder:
arch: amd64
context: .
dockerfile: Dockerfile
env:
clear:
N8N_HOST: n8n.yourcompany.com
N8N_PORT: "5678"
N8N_PROTOCOL: https
WEBHOOK_URL: https://n8n.yourcompany.com/
GENERIC_TIMEZONE: Australia/Brisbane
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: "5432"
DB_POSTGRESDB_DATABASE: n8n
DB_POSTGRESDB_USER: n8n
EXECUTIONS_DATA_PRUNE: "true"
EXECUTIONS_DATA_MAX_AGE: "168"
secret:
- DB_POSTGRESDB_PASSWORD
- N8N_ENCRYPTION_KEY
volumes:
- "/srv/n8n_data:/home/node/.n8n"
accessories:
postgres:
image: postgres:16
host: 203.0.113.10
port: "127.0.0.1:5432:5432"
env:
clear:
POSTGRES_USER: n8n
POSTGRES_DB: n8n
secret:
- POSTGRES_PASSWORD
files:
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
directories:
- /srv/postgres_data:/var/lib/postgresql/data
A few details worth calling out. EXECUTIONS_DATA_PRUNE with EXECUTIONS_DATA_MAX_AGE set to 168 hours keeps your database from quietly growing into a problem. The single biggest debugging session we have lost time on with self-hosted n8n was a Postgres instance that had quietly accumulated three years of execution logs and was making every workflow page load take fifteen seconds. Set this on day one.
The volumes mount on the host is critical. /srv/n8n_data stores the encryption key, the SQLite fallback, and any binary data n8n writes. Back this up. We will talk about backups later.
Building and Pushing the Image
Kamal will build from a Dockerfile in your project. For n8n you do not actually need a custom image. The official n8nio/n8n image is what runs in production for almost everyone. Use it directly:
FROM n8nio/n8n:1.94.0
# Pin the version. The 'latest' tag has bitten us twice.
Pin the version. We have rolled back exactly twice in three years of running n8n, both times because latest shipped a breaking change to a node we depended on. The five seconds you save not pinning is not worth the half day of unpicking why your workflows started failing at 2am.
Push your image and run the initial setup:
kamal setup
# This installs Docker on the server, pulls kamal-proxy,
# starts Postgres, builds and pushes your image, and
# starts n8n. About 4-6 minutes on a fresh server.
kamal deploy
# Subsequent deploys. About 30 seconds for a no-change deploy,
# 90 seconds for a real version bump.
If kamal setup fails partway through, fix the issue and run it again. Kamal is mostly idempotent. The exception is the registry login, which sometimes needs kamal registry login run manually first.
Domain, SSL, and the Reverse Proxy
The proxy: ssl: true directive in the deploy.yml triggers automatic Let’s Encrypt certificate provisioning through kamal-proxy. For this to work, your DNS must be set before you run kamal deploy. Set an A record from n8n.yourcompany.com to your server’s IP, wait until dig resolves, then deploy.
The proxy handles HTTP-to-HTTPS redirect, certificate renewal, and basic health checks. It listens on 80 and 443 on the host. There is no Caddy, no Traefik, no nginx config to write. This is the upgrade we mentioned earlier; the kamal-proxy binary is small, fast, and has not given us a single SSL outage in 18 months across about a dozen client deployments.
If you need IP allowlisting or basic auth in front of n8n, kamal-proxy does not currently handle this in a clean way. We use Cloudflare Access for one client and a small WARP-based VPN for another, both sitting in front of the public domain. Putting a real Caddy or nginx between Kamal and n8n is also possible but loses some of Kamal’s simplicity.
Database, Persistence, and the Encryption Key
Three things live on the host that you must protect. Lose any of them and you lose data that cannot be reconstructed from your image.
First, the Postgres data directory at /srv/postgres_data. This is your workflow definitions, credentials, executions, and binary references. We back this up nightly with pg_dump piped to a B2 bucket via rclone. Cron runs at 02:00 Brisbane time. The dump is roughly 50 MB compressed for a moderately busy n8n instance and the bucket lifecycle keeps 30 daily, 12 weekly, and 24 monthly snapshots.
Second, the n8n data volume at /srv/n8n_data. This holds the SQLite fallback (which you should not be using if Postgres is configured, but it gets created anyway), the binary data referenced by workflow runs, and most importantly the config file containing the encryption key.
Third, the encryption key itself. n8n encrypts every credential in the database with this key. If you lose the key but keep the database, every credential is gone and you have to manually re-enter every API token, every OAuth refresh token, every SMTP password. We have done this once, for a client whose previous developer had not backed up the key. It took two weeks. The encryption key lives in 1Password for every client we run; treat it like the production database password it effectively is.
Set the key explicitly in your secrets file (Kamal 2 uses .kamal/secrets with whatever encrypted secrets backend you choose) rather than letting n8n generate one on first boot. That way you control it and can move the deployment between hosts.
Production Patterns We Use
The deploy.yml above gets you running. Production needs a few more things on top.
Separate Postgres host
Once you cross about a hundred workflow executions per minute, the database wants its own machine. We move clients to a managed Postgres (DigitalOcean Managed Databases or AWS RDS) and remove the accessories.postgres block from the Kamal config, replacing the host in DB_POSTGRESDB_HOST with the managed instance address. Backups become the provider’s problem.
Queue mode with Redis
For high-volume workflows or anything with long-running sub-tasks, n8n’s queue mode is essential. Add a Redis accessory and a separate worker container with EXECUTIONS_MODE: queue. Kamal handles multi-role deploys reasonably; the syntax is verbose but the behaviour is clean once it works.
Version pinning and the update cadence
n8n ships weekly. We update production every two weeks for active clients, after running the new version against a staging instance for at least 24 hours. Kamal makes this trivial: bump the version in the Dockerfile, commit, run kamal deploy. Total downtime per update is roughly four seconds, taken by the proxy’s drain phase.
Logs and observability
Kamal’s kamal app logs --follow is fine for debugging a specific issue. For ongoing monitoring we ship logs to Better Stack (formerly Logtail) using a Vector sidecar, and use Better Stack’s uptime check on /healthz. Total cost: about $20 AUD per month per environment.
When Kamal Is Not the Right Choice
The honest list. We move clients off Kamal when:
The team does not have a Ruby developer. Kamal is written in Ruby and the failure modes are easier to debug if at least one person on the team can read the gem source. If your team is all Python and you are deploying one service, Coolify’s UI gives you most of Kamal’s value without the Ruby toolchain.
You only have one or two services. The whole point of Kamal is unifying deploys across many services. For a single n8n install, the official Docker Compose file is shorter than your deploy.yml and just as reliable. Our Docker Compose guide covers the alternative.
You want a UI. Kamal is CLI-only. Some clients (specifically smaller in-house teams without a dedicated ops person) want to push a button to deploy. Coolify does this well.
You need autoscaling or want to never see a server. Kamal assumes you have an actual machine you log into. Fly.io is the better choice if you want fully managed.
You are running n8n at very small scale. If you have five workflows that fire once a day, Kamal is overkill. Coolify or even n8n Cloud (free tier or $20/month plan) is fine.
Costs in AUD: Realistic Numbers
What you pay depends almost entirely on your VPS, because Kamal itself is free. Sydney-region pricing as of May 2026, single n8n instance, modest workload (a few hundred runs a day):
Add a managed Postgres if you go that route. DigitalOcean’s smallest is around $25 AUD per month. RDS in ap-southeast-2 starts at about $30 AUD per month for a t4g.micro. For clients running anything with regulated data we prefer ap-southeast-2 (Sydney) or ap-southeast-4 (Melbourne).
Total realistic monthly cost for a Kamal-deployed n8n with managed Postgres: $45-$80 AUD per month. Without managed Postgres (running it on the same box, accepting your own backup responsibility): $20-$30 AUD per month. Compare that to n8n Cloud’s pricing if you have not already; the break-even point is much lower than most people expect.
If you want a steer on which architecture suits your workload, book a call and we will talk through it without trying to sell you anything you do not need.
Migrating from Docker Compose to Kamal
If you already have n8n running on Docker Compose and you have decided Kamal is right for you, the migration is roughly:
- On the source server, take a Postgres dump and a tar of the n8n data volume. Verify the encryption key is recoverable from the volume.
- Provision a new server. Install Docker. Run
kamal setupfrom your local machine pointing at it. - Stop the new n8n container, restore the Postgres dump into the Kamal-managed Postgres accessory, restore the n8n data volume to
/srv/n8n_dataon the new host. - Start n8n. Verify a known workflow runs without errors. Check that credentials are decrypting (this proves the encryption key migrated correctly).
- Update DNS. Wait for propagation. Decommission the old server only after 48 hours of confirmed stable runs.
Total time for a clean migration of a small n8n instance: about three hours including DNS wait. We have done this for clients moving from a deprecated VPS provider, from a free tier that had hit its limits, and from a previous developer’s setup that was no longer maintainable.
Things That Broke for Us
Three production gotchas worth knowing about.
The proxy network upgrade in Kamal 2. Migrating a 1.x deploy to 2.x requires a clean teardown of the proxy. The migration script handled it cleanly for two clients and partially for a third. The fix was 15 minutes of docker network prune and a kamal proxy reboot. Read the upgrade notes before you run it.
Postgres accessory health checks. Kamal does not natively health-check accessory containers the way it checks the app. If Postgres dies, n8n keeps trying to connect and fills the logs with errors. We added a separate uptime check against the host’s port 5432 to alert us when this happens. Took us one outage to learn this.
Disk fills up from execution data. Even with EXECUTIONS_DATA_PRUNE on, binary data references inside /srv/n8n_data can grow unbounded if your workflows process files. We add a weekly cron that find /srv/n8n_data/binaryData -mtime +14 -delete as a safety net.
Frequently Asked Questions
Is Kamal better than Docker Compose for n8n?
For a single-server, single-service deployment, no. Docker Compose is simpler, more familiar, and does the job. Kamal becomes worth it once you are deploying to multiple hosts, want zero-downtime updates, or are running it as a consistent deploy tool across many services. Most of our small-business clients are happier on Compose. Most of our larger clients use Kamal or Coolify.
How much does it cost to host n8n with Kamal in Australia?
The Kamal tooling itself is free. Hosting in Sydney with a small VPS like Vultr or DigitalOcean costs $18-$25 AUD per month for a 2 GB instance, plus $25-$30 AUD per month if you add managed Postgres. Total for a production-grade single-node deployment: around $50 AUD per month. International providers like Hetzner are cheaper if higher latency to Australian users is acceptable.
Does Kamal handle zero-downtime deploys for n8n?
Effectively yes. The kamal-proxy drains the old container while the new one warms up, and traffic switches once the new container passes its health check. In practice we see about 4 seconds of slow requests during a switch. For webhook-triggered workflows this is rarely visible. For scheduled workflows it is invisible.
Can I run multiple n8n instances with Kamal?
Yes. Add hosts to the servers.web.hosts array and Kamal will deploy to each. For n8n specifically you must use queue mode and a shared Redis when running multiple workers, otherwise scheduled triggers will fire on every node. Single-host plus a separate worker is the simpler architecture and covers most use cases.
What happens if I lose my n8n encryption key?
Every credential stored in the database becomes unreadable. Workflows referencing those credentials will fail. There is no recovery path other than re-entering each credential by hand. Always store the encryption key separately from the database backup. We use 1Password; some clients use AWS Secrets Manager. The mistake to avoid is leaving it only in the n8n data volume on a server you might lose.
How do I update n8n when using Kamal?
Bump the n8n version in your Dockerfile, commit the change, and run kamal deploy. Kamal builds the new image, pushes it to your registry, pulls it on each host, starts the new container, waits for the health check to pass, then swaps traffic. The whole cycle takes around 90 seconds for a version bump. Always test the new version on a staging deploy first; n8n is mostly backwards-compatible but credential and node API changes do happen.
Should I use Kamal 2 or stay on Kamal 1?
Use Kamal 2. The kamal-proxy is materially more reliable than the Traefik-based 1.x proxy, the secrets handling is cleaner, and accessory deploys are more predictable. The only reason to stay on 1.x is an existing setup you have not had time to migrate. Plan an afternoon for the upgrade, take a backup first, and read the Kamal upgrade notes carefully.
Can Kamal deploy n8n with Postgres, Redis, and workers in one config?
Yes, using accessories for Postgres and Redis and a separate role for the worker. The deploy.yml gets long and noisy at this point, which is a fair signal that you are pushing Kamal’s sweet spot. We typically run this exact architecture for moderately busy n8n deployments and it works fine, but if you find yourself with five accessories and three roles, consider whether something like Coolify or a small Kubernetes cluster might serve you better.
If you want help choosing the right hosting architecture for n8n, or you have an existing deployment you want reviewed, get in touch. We are based in Brisbane and have set up n8n on most of the platforms worth considering, including Kamal where it earned its spot.
Jump to a section
Ready to streamline your operations?
Get in touch for a free consultation to see how we can streamline your operations and increase your productivity.