How to self-host n8n using Docker
Table of Contents
On this page
n8n is a popular and open-source workflow automation tool, which you can self-host on a UpCloud Cloud server using Docker Compose and Caddy as a reverse proxy. This detailed tutorial ensures a smooth setup while highlighting best practices for security and maintenance.
What you need
Before starting, ensure you meet these requirements:
A VPS running Ubuntu 22.04 or 24.04
Root sudo SSH access to the server
A domain name you control (e.g. example.com)
DNS A-record pointed at your VPS IP
Ports 80 and 443 reachable from the internet
~1 GB RAM minimum (2 GB recommended)
Connect to your VPS & update packages
SSH into your VPS as root or a sudo user, then make sure all packages are up to date before installing anything.
1# Connect from your local machine
2ssh root@YOUR_SERVER_IP
3
4# Update system packages
5apt update && apt -y upgradeTipp: Create a non-root sudo user for day-to-day use: adduser deploy && usermod -aG sudo deploy. This is a security best practice.
Install Docker CE & Docker Compose
Install Docker from the official Docker repository. This gives you the latest stable version plus the Compose plugin in one go.
1# Install prerequisites
2apt install -y ca-certificates curl gnupg lsb-release
3
4# Add Docker's official GPG key
5install -m 0755 -d /etc/apt/keyrings
6curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
7 | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
8chmod a+r /etc/apt/keyrings/docker.gpg
9
10# Add Docker's repository
11echo \
12 "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
13 https://download.docker.com/linux/ubuntu \
14 $(lsb_release -cs) stable" \
15 | tee /etc/apt/sources.list.d/docker.list > /dev/null
16
17# Install Docker CE + Compose plugin
18apt update
19apt install -y docker-ce docker-ce-cli containerd.io \
20 docker-buildx-plugin docker-compose-plugin1# Verify Docker is running
2docker --version
3docker compose version
4
5# (Optional) Run without sudo for your deploy user
6usermod -aG docker deployAfter adding your user to the docker group, log out and back in (or run newgrp docker) for the group change to take effect.
Configure the firewall
Open only the ports n8n needs. Keep SSH access open so you don't lock yourself out.
1# Allow SSH (critical — do this first!)
2ufw allow OpenSSH
3
4# Allow web traffic for Caddy
5ufw allow 80 # HTTP (Caddy redirects to HTTPS)
6ufw allow 443 # HTTPS
7
8# Enable the firewall
9ufw --force enable
10ufw statusNote: Always allow SSH before enabling UFW. Forgetting this locks you out of your server.
Point your domain to the VPS
In your domain registrar's DNS settings, create an A record for the subdomain you'll use for n8n:
1Type: A
2Host: n8n (or any subdomain you prefer)
3Value: YOUR_VPS_IP
4TTL: 3600Tipp: This routes n8n.yourdomain.com to your VPS. DNS changes can take a few minutes to propagate globally. You can verify with dig n8n.yourdomain.com.
Clone the n8n Docker setup
The n8n team maintains an official Docker + Caddy compose setup. Clone it and create the persistent data volumes.
1# Install git if not already present
2apt install -y git
3
4# Clone the official n8n Docker + Caddy repo
5git clone https://github.com/n8n-io/n8n-docker-caddy.git
6cd n8n-docker-caddy
7
8# Create persistent Docker volumes
9docker volume create caddy_data
10docker volume create n8n_dataConfigure environment variables
Edit the .env file in the cloned directory and fill in your values:
1nano .envVariable | Description & Example |
DOMAIN_NAME | Your root domain example.com |
SUBDOMAIN | Subdomain for n8n n8n |
N8N_HOST | Full hostname n8n.example.com |
N8N_PROTOCOL | Always use https in production https |
N8N_PORT | Internal container port (do not expose directly) 5678 |
N8N_ENCRYPTION_KEY | Unique secret for encrypting credentials — generate with
|
N8N_BASIC_AUTH_USER | Admin login username admin |
N8N_BASIC_AUTH_PASSWORD | Strong admin password — use a password manager |
Note: Never commit the .env file to version control. Add it to .gitignore immediately.
Configure Caddy as a reverse proxy
Open the Caddyfile and replace the domain placeholder with your actual subdomain. Caddy handles TLS certificate provisioning automatically via Let's Encrypt.
1nano caddy_config/Caddyfile1n8n.example.com {
2 reverse_proxy n8n:5678 {
3 flush_interval -1
4 }
5}Replace n8n.example.com with your actual hostname. Caddy will automatically obtain and renew an SSL certificate. No manual certbot configuration needed.
Launch n8n
Start all services in detached mode. Docker will pull the n8n and Caddy images, then start them in the background.
1# Start n8n + Caddy in the background
2docker compose up -d
3
4# Watch logs to confirm everything started cleanly
5docker compose logs -fAfter a minute, visit https://n8n.yourdomain.com in your browser. You should see the n8n login screen.
Tipp: Useful management commands below. Bookmark these.
1# Stop all services
2docker compose down
3
4# Pull latest images and restart (for updates)
5docker compose pull && docker compose up -d
6
7# View running containers
8docker compose ps
9
10# Restart just n8n
11docker compose restart n8nAuto-start on reboot (optional but recommended)
Enable Docker to start automatically on server reboot, so n8n comes back up without manual intervention.
1# Enable Docker daemon on boot
2systemctl enable docker
3
4# Ensure restart policy is set in docker-compose.yml
5# (the n8n-docker-caddy repo includes restart: unless-stopped by default)
6grep restart docker-compose.ymlTipp: The official n8n compose file already sets restart: unless-stopped on all services, so containers will restart automatically after a reboot or crash.
Troubleshooting
In the following I want to show you a few common errors you might encounter and how to fix them.
Common issues
1# SSL certificate not issuing
2→ Ensure ports 80 and 443 are open and DNS is propagated
3 Run: dig n8n.yourdomain.com
4
5# n8n not accessible after starting
6→ Check logs: docker compose logs caddy
7→ Check logs: docker compose logs n8n
8
9# Permission denied on docker command
10→ Add user to docker group: usermod -aG docker $USER
11→ Then log out and back in, or run: newgrp docker
12
13# Port already in use
14→ Check what's using port 80/443: ss -tlnp | grep ':80\|:443'
