If you want a self-hosted automation stack that won’t disappear behind a paywall or rate-limit your webhooks, n8n on Docker with PostgreSQL is a solid choice. The whole setup takes about 20 minutes and gives you a production-ready environment with persistent storage, HTTPS, and automatic SSL renewal.
This guide walks through every step: Docker installation, Compose configuration, Nginx reverse proxy setup, and Let’s Encrypt certificate issuance. You’ll end up with n8n running securely at your own domain.
Prerequisites
- Ubuntu 26.04 server (root or sudo-privileged user)
- A domain name with an A record pointing to your server’s IP address
Step 1: Update System Packages
Before touching anything else, bring all packages current:
apt update -y && apt upgrade -yStep 2: Install Docker

Install the required dependencies first:
apt install software-properties-common apt-transport-https ca-certificates -yAdd the official Docker GPG key:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpgRegister the Docker repository:
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullUpdate the package index and install Docker:
apt update -y
apt install docker-ce docker-ce-cli containerd.io -yStart the service and set it to launch on boot:
systemctl start docker && systemctl enable dockerConfirm it’s running:
systemctl status dockerYou should see Active: active (running) in the output before moving on.
️ Step 3: Create the Project Directory and Config Files
Create a dedicated directory for the project:
mkdir ~/n8n
cd ~/n8nThe .env file
Create the environment file and fill in your credentials and domain:
nano .envPOSTGRES_USER=n8n
POSTGRES_PASSWORD=StrongPostgresPasswordHere
POSTGRES_DB=n8n
N8N_BASIC_AUTH_ACTIVE=true
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=Strongn8nAdminPasswordHere
N8N_HOST=yourdomain.com
N8N_PORT=5678
N8N_PROTOCOL=https
WEBHOOK_URL=https://yourdomain.com/Replace StrongPostgresPasswordHere, Strongn8nAdminPasswordHere, and yourdomain.com with your actual values before saving.
The docker-compose.yml file
Create the Compose file:
nano docker-compose.ymlservices:
postgres:
image: postgres:16
container_name: n8n-postgres
restart: always
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- n8n-network
n8n:
image: docker.n8n.io/n8nio/n8n
container_name: n8n
restart: always
ports:
- "5678:5678"
environment:
DB_TYPE: postgresdb
DB_POSTGRESDB_HOST: postgres
DB_POSTGRESDB_PORT: 5432
DB_POSTGRESDB_DATABASE: ${POSTGRES_DB}
DB_POSTGRESDB_USER: ${POSTGRES_USER}
DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE}
N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER}
N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD}
N8N_HOST: ${N8N_HOST}
N8N_PORT: ${N8N_PORT}
N8N_PROTOCOL: ${N8N_PROTOCOL}
WEBHOOK_URL: ${WEBHOOK_URL}
GENERIC_TIMEZONE: America/Chicago
volumes:
- n8n_data:/home/node/.n8n
depends_on:
- postgres
networks:
- n8n-network
volumes:
postgres_data:
n8n_data:
networks:
n8n-network:Save and close the file. The depends_on directive ensures PostgreSQL starts before n8n tries to connect.
Step 4: Start the Containers

Spin up both containers in detached mode:
docker compose up -dOnce it finishes, verify both containers are running:
docker psYou should see two entries: n8n running on port 5678 and n8n-postgres on port 5432. You can reach the n8n interface at http://YourServerIPAddress:5678 or http://YourDomainName.com:5678 at this point, but you’ll see a security warning. The next step clears that.
Step 5: Set Up Nginx as a Reverse Proxy with HTTPS
Install Nginx:
apt install nginx -y
systemctl start nginx && systemctl enable nginxCreate the Nginx config for n8n:
nano /etc/nginx/sites-available/n8n.confserver {
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:5678;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}Enable the config and verify it:
ln -s /etc/nginx/sites-available/n8n.conf /etc/nginx/sites-enabled/
nginx -tA successful test returns syntax is ok and test is successful. Restart Nginx:
systemctl restart nginxIssue the SSL certificate
Install Certbot with the Nginx plugin:
apt install certbot python3-certbot-nginx -yRequest a free Let’s Encrypt certificate for your domain:
certbot --nginx -d yourdomain.comCertbot will ask for an email address and terms acceptance. Once complete, it automatically updates your Nginx config for HTTPS and schedules automatic renewals. Your n8n instance is now accessible securely at https://yourdomain.com.
✅ Step 6: Complete the n8n Setup Wizard
Open your domain in a browser. You’ll walk through a short setup wizard:
- Create the owner account by filling in your name, email, and password, then click Next.
- Enter your company details on the following screen.
- Skip the license screen if you don’t need paid features.
- You’ll land on the n8n admin dashboard, ready to build workflows.
Common Pitfalls
- DNS not propagated yet: Certbot will fail if your A record hasn’t resolved to your server IP. Confirm with
dig yourdomain.combefore running Certbot. - Port 5678 blocked by firewall: If you can’t reach n8n directly before setting up Nginx, check that your firewall allows port 5678 (and 80/443 for Nginx).
- Wrong timezone in workflows: The Compose file sets
GENERIC_TIMEZONE: America/Chicago. Change this to your timezone before starting the containers if you’re scheduling time-based workflows. - Weak passwords in .env: The
.envfile holds your Postgres and n8n admin credentials in plain text. Lock down file permissions withchmod 600 .envafter creation.


