Table of Contents
What you will read?
n8n is a powerful open-source automation platform. Ubuntu 25.10 runs it reliably with Docker, PostgreSQL for persistence, and HTTPS via a reverse proxy. The steps below set up a production-ready instance with a custom domain, automatic SSL certificates, and auto-start on boot. Terminal access and a pointed DNS record are assumed.
Update Ubuntu 25.10
Apply the latest security patches and confirm the release version.
sudo apt update && sudo apt -y upgrade
. /etc/os-release && echo "$PRETTY_NAME"
Ubuntu 25.10 (Kinetic Kudu)
Set DNS and hostname
Point your domain’s A/AAAA record to the server IP, then set a matching hostname.
# Replace with your domain
export N8N_DOMAIN="n8n.example.com"
# Local hostname (optional but recommended)
sudo hostnamectl set-hostname "$N8N_DOMAIN"
# Check DNS from the server (should resolve to your public IP)
dig +short A "$N8N_DOMAIN"
dig +short AAAA "$N8N_DOMAIN"
Install Docker and Compose
Install Docker Engine and the Compose plugin from Docker’s official repository.
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Optional: run docker without sudo
sudo usermod -aG docker $USER
newgrp docker
docker --version
docker compose version
Create directories and secrets
Prepare persistent volumes and strong secrets for n8n and PostgreSQL.
sudo mkdir -p /opt/n8n/{data,postgres,caddy}
sudo chown -R $USER:$USER /opt/n8n
cd /opt/n8n
# Generate strong secrets
openssl rand -base64 48 | tr -d '\n' ; echo
openssl rand -base64 36 | tr -d '\n' ; echo
Create an .env file with production variables.
cat > /opt/n8n/.env <<'ENV'
# Domain and contact
N8N_HOST=n8n.example.com
N8N_PROTOCOL=https
N8N_PORT=5678
WEBHOOK_URL=https://n8n.example.com/
# Security and behavior
NODE_ENV=production
GENERIC_TIMEZONE=UTC
N8N_SECURE_COOKIE=true
N8N_DIAGNOSTICS_ENABLED=false
N8N_ENCRYPTION_KEY=REPLACE_WITH_32_64_CHAR_SECRET
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=REPLACE_WITH_STRONG_PASSWORD
# Database (PostgreSQL)
DB_TYPE=postgresdb
POSTGRES_USER=n8n
POSTGRES_PASSWORD=REPLACE_WITH_STRONG_DB_PASSWORD
POSTGRES_DB=n8n
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
DB_POSTGRESDB_USER=${POSTGRES_USER}
DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
ENV
Create docker-compose.yml
Define containers for PostgreSQL, n8n, and Caddy (automatic HTTPS).
cat > /opt/n8n/docker-compose.yml <<'YML'
version: "3.8"
services:
postgres:
image: postgres:16
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- ./postgres:/var/lib/postgresql/data
networks: [n8n]
restart: unless-stopped
n8n:
image: n8nio/n8n:latest
depends_on: [postgres]
environment:
- DB_TYPE=${DB_TYPE}
- DB_POSTGRESDB_HOST=${DB_POSTGRESDB_HOST}
- DB_POSTGRESDB_PORT=${DB_POSTGRESDB_PORT}
- DB_POSTGRESDB_DATABASE=${DB_POSTGRESDB_DATABASE}
- DB_POSTGRESDB_USER=${DB_POSTGRESDB_USER}
- DB_POSTGRESDB_PASSWORD=${DB_POSTGRESDB_PASSWORD}
- N8N_HOST=${N8N_HOST}
- N8N_PROTOCOL=${N8N_PROTOCOL}
- N8N_PORT=${N8N_PORT}
- WEBHOOK_URL=${WEBHOOK_URL}
- NODE_ENV=${NODE_ENV}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- N8N_SECURE_COOKIE=${N8N_SECURE_COOKIE}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_DIAGNOSTICS_ENABLED=${N8N_DIAGNOSTICS_ENABLED}
- N8N_BASIC_AUTH_ACTIVE=${N8N_BASIC_AUTH_ACTIVE}
- N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
volumes:
- ./data:/home/node/.n8n
networks: [n8n]
restart: unless-stopped
caddy:
image: caddy:2-alpine
depends_on: [n8n]
ports:
- "80:80"
- "443:443"
environment:
- ACME_AGREE=true
- [email protected]
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
networks: [n8n]
restart: unless-stopped
networks:
n8n:
driver: bridge
volumes:
caddy_data:
caddy_config:
YML
Create Caddyfile for HTTPS
Tell Caddy to request a certificate and proxy traffic to n8n.
mkdir -p /opt/n8n/caddy
cat > /opt/n8n/caddy/Caddyfile <<CF
{
email [email protected]
}
n8n.example.com {
encode zstd gzip
tls {
protocols tls1.2 tls1.3
}
reverse_proxy n8n:5678
}
CF
Launch containers
Pull the images, start the stack, and watch logs for healthy status.
cd /opt/n8n
docker compose pull
docker compose up -d
docker compose ps
docker compose logs -f --tail=100 n8n
✔ n8n Started
Editor is now accessible via:
https://n8n.example.com/
Allow firewall ports
Open HTTP/HTTPS while keeping SSH access.
sudo ufw allow OpenSSH
sudo ufw allow 80,443/tcp
sudo ufw --force enable
sudo ufw status
Enable auto-start with systemd
Create a unit so the stack starts on boot and can be managed like a service.
sudo tee /etc/systemd/system/n8n-stack.service >/dev/null <<'UNIT'
[Unit]
Description=n8n stack (Docker Compose)
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target
[Service]
Type=oneshot
WorkingDirectory=/opt/n8n
RemainAfterExit=true
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
[Install]
WantedBy=multi-user.target
UNIT
sudo systemctl daemon-reload
sudo systemctl enable --now n8n-stack
systemctl status n8n-stack --no-pager
Update and backup
Upgrade n8n safely and back up the database regularly.
# Update images and restart
cd /opt/n8n
docker compose pull
docker compose up -d
docker image prune -f
# Backup Postgres (creates SQL dump with date stamp)
set -a; . /opt/n8n/.env; set +a
docker compose exec -T postgres pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB" > "/opt/n8n/postgres/backup-$(date +%F).sql"
Optional: Native Node.js install
For lab use without Docker, install Node.js LTS and run n8n locally (SQLite, no reverse proxy).
sudo apt update
sudo apt install -y curl
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs build-essential
sudo npm i -g n8n
# Run n8n on port 5678
export N8N_ENCRYPTION_KEY="$(openssl rand -base64 48)"
export N8N_BASIC_AUTH_ACTIVE=true
export N8N_BASIC_AUTH_USER=admin
export N8N_BASIC_AUTH_PASSWORD='REPLACE_ME'
n8n start
Installation complete. n8n now runs securely on Ubuntu 25.10 with Docker, PostgreSQL, and automatic HTTPS. For more study and guidance, and to buy servers with support, you can use dropvps.
