An autopilot automation tool can go wrong: poor isolation on a shared VPS can compromise your production database. Here is how I deployed n8n in production for the TribuneJustice project — a legaltech platform I lead as Tech Lead — without disrupting existing infrastructure.
The Context: Making the Most of an Existing VPS
On TribuneJustice, I needed a workflow orchestrator for recurring tasks: notifications, service syncs, and scheduled jobs. Rather than paying for execution-based cloud subscriptions, I chose to self-host n8n on a VPS already running for the project — maximizing existing resources rather than multiplying infrastructure costs.
Architecture Choice: Complete Isolation
- Dedicated PostgreSQL 16 container (officially recommended by n8n over MySQL)
- Isolated Docker network connecting only n8n and its PostgreSQL instance
- n8n exposed exclusively on
127.0.0.1:5678, never directly to the public web - Existing Nginx configured with a dedicated
server_blockforn8n.samensteeve.com - CPU and memory limits on containers to protect main project resources
Step-by-Step Deployment
sudo mkdir -p /opt/n8n
cd /opt/n8n
sudo mkdir -p n8n_data postgres_dataN8N_HOST=n8n.samensteeve.com
N8N_PROTOCOL=https
N8N_WEBHOOK_URL=https://n8n.samensteeve.com/
N8N_ENCRYPTION_KEY=<generated_key>
GENERIC_TIMEZONE=Africa/Douala
TZ=Africa/Douala
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=n8n_user
DB_POSTGRESDB_PASSWORD=<strong_password>
POSTGRES_USER=n8n_user
POSTGRES_PASSWORD=<same_strong_password>
POSTGRES_DB=n8nservices:
postgres:
image: postgres:16-alpine
container_name: n8n_postgres
restart: always
env_file: .env
volumes:
- ./postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
networks:
- n8n_net
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: always
env_file: .env
ports:
- "127.0.0.1:5678:5678"
volumes:
- ./n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
networks:
- n8n_net
networks:
n8n_net:
driver: bridgeReal-World Debugging Lessons
Three specific issues arose during deployment: volume file permissions (resolved via chown -R 1000:1000), initial Postgres credential mismatch, and a Google Safe Browsing false positive on the newly created login subdomain.
Figure 1: Heuristic Chrome / Google Safe Browsing warning upon first accessing the n8n subdomain.
Once the Google false positive was cleared via Search Console review, the n8n instance became fully accessible and ready for workflow automation.
Figure 2: Secured, fully operational n8n instance on n8n.samensteeve.com.
Takeaways
Proper self-hosting requires robust isolation, security hardening, and proper reverse proxy configuration. It ensures cost-effective workflow automation without compromising production reliability.