4/5/2026 · 14 min read
Self-Hosting n8n on Digital Ocean: A 30-Minute Step-by-Step Guide
The exact steps I use to deploy production n8n for clients. Includes domain setup, SSL, backups, and the security mistakes most people miss.
This is the playbook I run every time a client says "I don't want to pay n8n cloud, can we self-host?" By the end you'll have a production-ready n8n instance with HTTPS, daily backups, and basic auth at a cost of about $6/month.
What you'll need
- A Digital Ocean account ($200 free credit if you're new)
- A domain (n8n.yourdomain.com works fine — you don't need a separate domain)
- 30 minutes
- Basic SSH comfort
Step 1: Create the droplet
In Digital Ocean dashboard:
- Distribution: Ubuntu 24.04 LTS
- Plan: Basic, Regular, $6/mo (1GB RAM, 25GB SSD)
- Datacenter: pick the one closest to your team
- Authentication: SSH key (paste your public key, don't use password auth)
- Hostname:
n8n-prod
Click create. Takes ~60 seconds.
Step 2: Point your domain
In your DNS provider (Cloudflare, Namecheap, whatever):
- Type: A record
- Name:
n8n - Value: your droplet's public IP
If you're on Cloudflare, set it to DNS-only (grey cloud) for now — you'll re-enable proxy after SSL is working.
Step 3: SSH in and prep the server
ssh root@YOUR_DROPLET_IP
# Update everything
apt update && apt upgrade -y
# Create a non-root user
adduser deploy
usermod -aG sudo deploy
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy
# Install Docker
curl -fsSL https://get.docker.com | sh
usermod -aG docker deploy
# Switch to deploy user
su - deployStep 4: Set up the n8n stack
Create /home/deploy/n8n/docker-compose.yml:
services:
traefik:
image: traefik:v3.0
command:
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --certificatesresolvers.le.acme.email=${LETSENCRYPT_EMAIL}
- --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.le.acme.tlschallenge=true
ports:
- "80:80"
- "443:443"
volumes:
- traefik_data:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
restart: always
n8n:
image: docker.n8n.io/n8nio/n8n
restart: always
environment:
- N8N_HOST=${N8N_HOST}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://${N8N_HOST}/
- GENERIC_TIMEZONE=${TZ}
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=${N8N_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_PASS}
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- n8n_data:/home/node/.n8n
labels:
- traefik.enable=true
- traefik.http.routers.n8n.rule=Host(`${N8N_HOST}`)
- traefik.http.routers.n8n.entrypoints=websecure
- traefik.http.routers.n8n.tls.certresolver=le
- traefik.http.services.n8n.loadbalancer.server.port=5678
depends_on:
- postgres
postgres:
image: postgres:16
restart: always
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
n8n_data:
postgres_data:
traefik_data:Create .env:
N8N_HOST=n8n.yourdomain.com
N8N_USER=admin
N8N_PASS=use-a-strong-random-password-here
LETSENCRYPT_EMAIL=you@yourdomain.com
TZ=Asia/Karachi
POSTGRES_DB=n8n
POSTGRES_USER=n8n
POSTGRES_PASSWORD=another-strong-password-hereSpin it up:
docker compose up -d
docker compose logs -f n8nWait ~30 seconds for SSL certs to issue. Then visit https://n8n.yourdomain.com. You'll get the basic auth prompt, then n8n's setup screen.
Step 5: Backups (don't skip this)
Create /home/deploy/backup.sh:
#!/bin/bash
BACKUP_DIR=/home/deploy/backups
mkdir -p $BACKUP_DIR
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
docker exec n8n-postgres-1 pg_dump -U n8n n8n | gzip > $BACKUP_DIR/n8n_$TIMESTAMP.sql.gz
find $BACKUP_DIR -name "n8n_*.sql.gz" -mtime +7 -deleteMake it executable, add to cron:
chmod +x /home/deploy/backup.sh
crontab -e
# Add: 0 3 * * * /home/deploy/backup.shFor offsite backup, pipe the dump to S3 / R2 / Backblaze instead of local disk.
Security mistakes to avoid
- Don't use the default
editoruser. Set basic auth or, better, put it behind Cloudflare Access for SSO. - Don't expose port 5678 directly. Traefik handles HTTPS — port 5678 should not be in your firewall rules.
- Don't run n8n as root. The compose file above runs as the
nodeuser inside the container. - Encrypt your
.env. Use Doppler, Infisical, or at minimum chmod 600. - Set up firewall:
ufw allow 22 && ufw allow 80 && ufw allow 443 && ufw enable.
When to upgrade past $6/mo
If you're running >50 workflows or >10K executions/day, bump to the $12 droplet (2GB RAM). I've never needed bigger than $24/mo for any client.
That's it. Production n8n in 30 minutes.
Like this post?
Subscribe for weekly automation breakdowns and production templates.
Join newsletter