Comparing n8n Workflow Automation Deployment Models
When engineering teams need to interconnect GitLab, Jira, Slack, and internal CRM systems without paying $50 to hundreds of dollars a month for Zapier or Make, n8n is always a top contender. However, choosing the right infrastructure to run n8n is a question that requires careful consideration before rolling it into production:
- Model 1: n8n Cloud (SaaS) — Pay a monthly subscription (starting from €20/month for 2,500 executions) to run directly on the provider’s infrastructure without worrying about server configuration.
- Model 2: Single Docker Container with SQLite — Pull the
n8nio/n8nimage, mount a host directory, and run directly using the default SQLite database. - Model 3: Docker Compose Cluster with PostgreSQL — Separate the n8n application and PostgreSQL database into two distinct containers, managed centrally via environment variables and an internal Docker network.
Pros and Cons of Each Approach
Each option addresses specific requirements depending on execution traffic and data sensitivity:
1. n8n Cloud
- Pros: Zero infrastructure maintenance overhead. Automatic version updates, built-in SSL, and automated domain management.
- Cons: Costs escalate rapidly as workflow volume surges. Sensitive data such as API tokens and customer records must route through external servers.
2. Single Docker Container (SQLite)
- Pros: Up and running in 30 seconds with a single
docker runcommand. Ideal for tinkering with features or personal demos. - Cons: SQLite is prone to bottlenecks (database locking) when 10 to 20 webhooks fire concurrently. Additionally, backing up data when container issues arise is often complex and risky.
3. Docker Compose + PostgreSQL
- Pros: PostgreSQL smoothly handles hundreds of concurrent executions. You retain full control of your data on your VPS, can easily configure automated backups, and are ready to scale additional n8n workers as load increases.
- Cons: Requires foundational knowledge of Linux administration, Docker, and configuring a reverse proxy like Nginx.
In a past project, our team suffered an incident where an SQLite-backed n8n container crashed repeatedly during a flash sale due to file write bottlenecks. All payment gateway webhooks stalled for over 3 hours. The lesson was crystal clear: for any mission-critical production workload, decoupling the database into an independent PostgreSQL service is mandatory to prevent data loss or sudden service outages.
Why Docker Compose + PostgreSQL is the Optimal Choice for Production
If you want to build a reliable automation system for your entire team, the Docker Compose + PostgreSQL pairing delivers the best balance between cost and reliability.
A modest VPS (2 vCPUs, 4GB RAM costing around $10 – $15/month) can easily handle tens of thousands of triggers daily without I/O bottlenecks. The entire system configuration is also defined as code (IaC), making it easy to version control and synchronize via Git.
Step-by-Step Guide to Deploying n8n with Docker Compose and PostgreSQL
Step 1: Set Up Directories and Permissions
SSH into your VPS and create the persistent data directory structure:
mkdir -p /opt/n8n-stack/n8n_data /opt/n8n-stack/postgres_data
cd /opt/n8n-stack
# Set permissions for the node user inside the container (UID 1000)
sudo chown -R 1000:1000 n8n_data
Step 2: Create the .env Environment Configuration File
Isolate sensitive information into a .env file for easy management and to prevent credential leaks:
nano .env
Paste the following configuration into the file:
# Domain and timezone
DOMAIN_NAME=automation.yourdomain.com
GENERIC_TIMEZONE=Asia/Ho_Chi_Minh
# PostgreSQL database credentials
POSTGRES_USER=n8n_admin
POSTGRES_PASSWORD=MatKhauBaoMatCucManh_2026
POSTGRES_DB=n8n_db
# Credential encryption key (generate a random string of 32+ characters)
N8N_ENCRYPTION_KEY=b8f3e1a9c4d2095f8e7123456789abcd
Step 3: Compose the docker-compose.yml File
Create a docker-compose.yml file to define the postgres and n8n services:
services:
postgres:
image: postgres:16-alpine
container_name: n8n_postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- ./postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
networks:
- n8n_network
n8n:
image: n8nio/n8n:latest
container_name: n8n_app
restart: unless-stopped
ports:
- "127.0.0.1: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_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- TZ=${GENERIC_TIMEZONE}
- WEBHOOK_URL=https://${DOMAIN_NAME}/
- N8N_HOST=${DOMAIN_NAME}
- N8N_PORT=5678
- N8N_PROTOCOL=https
volumes:
- ./n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
networks:
- n8n_network
networks:
n8n_network:
driver: bridge
Step 4: Launch and Inspect Logs
Start the service cluster in detached mode:
docker compose up -d
Check container status and monitor startup logs:
docker compose ps
docker compose logs -f n8n
Once the logs display the ready state or Editor is now accessible via..., the application has successfully connected to the database.
Step 5: Set Up Nginx Reverse Proxy and SSL
Since port 5678 is only bound to 127.0.0.1 for security, you need Nginx as a reverse proxy to forward traffic and terminate SSL certificates. The Nginx configuration must support WebSockets so the n8n drag-and-drop UI can display real-time execution status:
server {
server_name automation.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;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_cache off;
}
}
Install a free SSL certificate via Let’s Encrypt Certbot using the command: sudo certbot --nginx -d automation.yourdomain.com. After that, you can navigate to your domain to set up the initial Owner account.
Step 6: Automate Daily Database Backups
To safeguard critical workflows and credentials, set up a cron job to automatically dump PostgreSQL data nightly:
# Open crontab
crontab -e
# Add the following line to run backups at 02:00 AM daily
0 2 * * * docker exec -t n8n_postgres pg_dump -U n8n_admin n8n_db | gzip > /opt/n8n-stack/backups/n8n_db_$(date +\%F).sql.gz
Your n8n cluster is now fully operational, running securely behind a reverse proxy with automated scheduled backups, and ready to power internal workflow automations for your entire team.

