Ubuntu 24.04 is now the go-to base for many VPS deployments. It’s stable, supported for years, and plays well with container workloads. If you want to run n8n in production, Docker and Caddy are a clean way to do it. Docker handles the app and dependencies, while Caddy gives you HTTPS and a reverse proxy without hours of manual config.
I’ll walk through a full installation, from a bare VPS to a working, SSL-enabled n8n instance. Along the way I’ll point out common pitfalls and some production tips.
Prerequisites
- A fresh Ubuntu 24.04 VPS (1 vCPU / 2GB RAM minimum; 2 vCPU / 4GB recommended for production).
- Root or sudo access via SSH.
- A domain name (optional but recommended) pointing to your VPS IP.
- Basic knowledge of Linux command line.
If you’d rather skip manual setup, you can always start from a ready-to-deploy n8n VPS with Docker and Caddy already preconfigured.
Buuuut…. let’s assume you’re doing this yourself 🙂
Step 1: Update and install dependencies
Always update first:
sudo apt update && sudo apt upgrade -y
Install the required packages:
sudo apt install -y curl git ufw
We’ll use UFW later to secure the firewall.
Step 2: Install Docker and Docker Compose
On Ubuntu 24.04, the Docker packages from the official repository are the best choice.
curl -fsSL https://get.docker.com | sudo sh
Add your user to the docker group (replace ubuntu
with your username):
sudo usermod -aG docker ubuntu
newgrp docker
Check versions:
docker --version
docker compose version
Docker Compose is now built into the Docker CLI, so you can use docker compose
directly.
Step 3: Set up project directory
Create a working directory for n8n:
mkdir ~/n8n && cd ~/n8n
Inside, create folders for data:
mkdir n8n_data db_data
Step 4: Configure Docker Compose
Here’s a production-ready Compose file using Postgres and Caddy:
services:
n8n:
image: n8nio/n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=changeme
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=strongpassword
- N8N_HOST=automation.example.com
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://automation.example.com/
- N8N_PORT=5678
volumes:
- ./n8n_data:/home/node/.n8n
depends_on:
- postgres
postgres:
image: postgres:15
restart: unless-stopped
environment:
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=changeme
- POSTGRES_DB=n8n
volumes:
- ./db_data:/var/lib/postgresql/data
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- ./caddy_data:/data
- ./caddy_config:/config
depends_on:
- n8n
Notes
- Replace
automation.example.com
with your domain. - Caddy will handle HTTPS automatically.
- For security, change the Postgres and n8n credentials before launch.
Step 5: Write the Caddyfile
Create Caddyfile
in the same directory:
automation.example.com {
reverse_proxy n8n:5678
}
If you don’t have a domain, you can skip Caddy and access n8n at http://your-vps-ip:5678
, but you won’t get SSL.
Step 6: Start the stack
Bring everything up:
docker compose up -d
Check logs:
docker compose logs -f n8n
After a few seconds, open your browser at https://automation.example.com
and you should see the n8n login page.
Step 7: Firewall setup
Block everything except SSH, HTTP, and HTTPS:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enable
Check with:
sudo ufw status verbose
Step 8: Backups and monitoring
This is the part most people forget. Postgres holds all workflows and execution history — if you lose it, you start from zero. At minimum:
- Use
pg_dump
to back up the database daily. - Snapshot your VPS or use a backup service.
- Enable incremental backups if your provider supports them.
For monitoring, I like Uptime Kuma for simple webhook checks and Grafana with Prometheus for deeper insights. See the Prometheus and Grafana monitoring guide for details.
Step 9: Going further
- Run in queue mode with Redis if you expect high throughput. That setup is explained in the Redis workers guide.
- Use private networking to keep Postgres and Redis off the public internet. Here’s a walkthrough: Private networking for n8n.
- Prune execution logs regularly to avoid database bloat. Here’s how to prune executions safely.
FAQ
Can I run this without Docker?
Yes, but it’s painful. Docker keeps everything isolated and reproducible.
Do I need a domain name?
Not strictly. You can run on IP only, but services like Stripe or GitHub require HTTPS callbacks. Domains plus Caddy solve this.
How do I upgrade later?
Pull the new image and restart:
docker compose pull n8n
docker compose up -d
Keep backups before upgrading.
Is 2GB RAM enough?
Yes, for light workloads. For anything beyond a few integrations, go 4GB+.
What if I don’t want to manage any of this?
That’s when a managed n8n VPS is worth it. It ships with Docker, Caddy, SSL, backups, and monitoring preconfigured so you can focus on workflows instead of infrastructure.