Skip to main content

N8N

Setup

  1. Create folder for n8n
mkdir n8n && cd n8n
  1. Create .env file and set the following variables
# Database
POSTGRES_USER=admin
POSTGRES_PASSWORD=changeme
POSTGRES_DB=n8n

POSTGRES_NON_ROOT_USER=n8n
POSTGRES_NON_ROOT_PASSWORD=changeme

# Configure
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=168
GENERIC_TIMEZONE=Asia/Taipei
WEBHOOK_URL=https://n8n.example.com
N8N_HOST=n8n.example.com
N8N_PROTOCOL=https
N8N_EMAIL_MODE=smtp
N8N_SMTP_HOST=smtp.gmail.com
N8N_SMTP_PORT=465
N8N_SMTP_USER=example@gmail.com
N8N_SMTP_PASS=PASSWORD
N8N_SMTP_SENDER=example@gmail.com
# N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
  1. Create init-data.sh file
#!/bin/bash
set -e;

if [ -n "${POSTGRES_NON_ROOT_USER:-}" ] && [ -n "${POSTGRES_NON_ROOT_PASSWORD:-}" ]; then
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE USER ${POSTGRES_NON_ROOT_USER} WITH PASSWORD '${POSTGRES_NON_ROOT_PASSWORD}';
GRANT ALL PRIVILEGES ON DATABASE ${POSTGRES_DB} TO ${POSTGRES_NON_ROOT_USER};
GRANT CREATE ON SCHEMA public TO ${POSTGRES_NON_ROOT_USER};
EOSQL
else
echo "SETUP INFO: No Environment variables given!"
fi
  1. Make init-data.sh executable
sudo chmod +x init-data.sh
  1. Create docker-compose.yml file
services:
postgres:
image: postgres:17-alpine
restart: always
environment:
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_DB
- POSTGRES_NON_ROOT_USER
- POSTGRES_NON_ROOT_PASSWORD
volumes:
- ./n8n-db:/var/lib/postgresql/data
- ./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh
healthcheck:
test: ['CMD-SHELL', 'pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
interval: 5s
timeout: 5s
retries: 10

n8n:
image: n8nio/n8n:latest
restart: unless-stopped
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_NON_ROOT_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD}
- EXECUTIONS_DATA_PRUNE=${EXECUTIONS_DATA_PRUNE}
- EXECUTIONS_DATA_MAX_AGE=${EXECUTIONS_DATA_MAX_AGE}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- WEBHOOK_URL=${WEBHOOK_URL}
- N8N_HOST=${N8N_HOST}
- N8N_PROTOCOL=${N8N_PROTOCOL}
- N8N_EMAIL_MODE=${N8N_EMAIL_MODE}
- N8N_SMTP_HOST=${N8N_SMTP_HOST}
- N8N_SMTP_PORT=${N8N_SMTP_PORT}
- N8N_SMTP_USER=${N8N_SMTP_USER}
- N8N_SMTP_PASS=${N8N_SMTP_PASS}
- N8N_SMTP_SENDER=${N8N_SMTP_SENDER}
expose:
- 5678
volumes:
- ./n8n-data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
  1. If you want to make N8N run under same network with NginxProxyManager, you can use the following code to docker-compose.yml
services:
postgres:
image: postgres:17-alpine
restart: always
environment:
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_DB
- POSTGRES_NON_ROOT_USER
- POSTGRES_NON_ROOT_PASSWORD
volumes:
- ./n8n-db:/var/lib/postgresql/data
- ./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh
healthcheck:
test: ['CMD-SHELL', 'pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}']
interval: 5s
timeout: 5s
retries: 10
networks:
- app-tier

n8n:
image: n8nio/n8n:latest
restart: unless-stopped
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_NON_ROOT_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD}
- EXECUTIONS_DATA_PRUNE=${EXECUTIONS_DATA_PRUNE}
- EXECUTIONS_DATA_MAX_AGE=${EXECUTIONS_DATA_MAX_AGE}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- WEBHOOK_URL=${WEBHOOK_URL}
- N8N_HOST=${N8N_HOST}
- N8N_PROTOCOL=${N8N_PROTOCOL}
- N8N_EMAIL_MODE=${N8N_EMAIL_MODE}
- N8N_SMTP_HOST=${N8N_SMTP_HOST}
- N8N_SMTP_PORT=${N8N_SMTP_PORT}
- N8N_SMTP_USER=${N8N_SMTP_USER}
- N8N_SMTP_PASS=${N8N_SMTP_PASS}
- N8N_SMTP_SENDER=${N8N_SMTP_SENDER}
expose:
- 5678
volumes:
- ./n8n-data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
networks:
- app-tier
- scoobydoo

networks:
app-tier:
driver: bridge
scoobydoo:
external: true
  1. Start the container
docker compose up -d

Update N8N

docker compose pull && docker compose stop && docker compose up -d

Reverse Proxy for N8N

  1. Create n8n.conf file
sudo vim /etc/nginx/sites-available/n8n.conf
  1. Add the following code to n8n.conf
server {
listen 80;
listen [::]:80;
listen 443 ssl http2;
listen [::]:443 ssl http2;

server_name n8n.example.com;
client_max_body_size 256M;

# SSL with Cloudflare
ssl_certificate /etc/ssl/certs/cf_example.com.pem;
ssl_certificate_key /etc/ssl/private/cf_key_example.com.pem;
ssl_client_certificate /etc/ssl/certs/origin-pull-ca.pem;
ssl_verify_client on;

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;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Frame-Options SAMEORIGIN;
proxy_buffers 256 16k;
proxy_buffer_size 16k;
client_max_body_size 50m;
client_body_buffer_size 1m;
proxy_read_timeout 600s;
proxy_buffering off;
proxy_cache off;
}

location ~ ^/(webhook|webhook-test) {
proxy_set_header Connection '';
chunked_transfer_encoding off;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Frame-Options SAMEORIGIN;
proxy_buffering off;
proxy_cache off;
proxy_pass http://127.0.0.1:5678;
}
}
  1. Create symbolic link
sudo ln -s /etc/nginx/sites-available/n8n.conf /etc/nginx/sites-enabled/n8n.conf
  1. Test nginx configuration
sudo nginx -t
  1. Restart nginx
sudo systemctl reload nginx

Backup & Restore

Docker

docker exec -it -u node {CONTAINER_ID} sh

Backup

Backup workflow

n8n export:workflow --all --output=.n8n/workflow.json

Backup credential

n8n export:credentials --all --decrypted --output=.n8n/credential.json

Restore

Import workflow

n8n import:workflow --input=.n8n/workflow.json

Import credential

n8n import:credentials --input=.n8n/credential.json