- Published on
Running Multiple n8n Instances with Traefik and Docker on Your Self-Hosted Server
- Authors
-
Hey there, fellow tech enthusiasts! 🚀 Today, I’ll share my journey into the world of running multiple n8n instances on my self-hosted server. Spoiler alert: it involves a bit of trial, a dash of error, and a whole lot of magic with Docker Compose and Traefik.
The Backstory
The backstory here unfolds in the dynamic world of client-centric setups. As I navigated through the landscape of self-hosted servers, the need arose to orchestrate distinct n8n instances for individual clients. The challenge? Ensuring a seamless separation of concerns and crafting an infrastructure that not only met the unique workflows of each client but also provided better support.
Picture it: each n8n instance a dedicated maestro,
The Challenge
In my current self-hosted server environment, I’ve successfully deployed an instance of n8n, a robust workflow automation tool, along with Traefik serving as a reverse proxy to manage incoming traffic. However, I’m encountering difficulties as I attempt to deploy a second instance of n8n.
Key Details:
- Port Mapping Challenge: n8n only exposes its internal port (5678), making it challenging to map different external ports for multiple instances.
- Traefik Configuration: Configuring Traefik to efficiently route traffic for multiple n8n instances based on domains or paths has proven to be more complex than anticipated.
- Server Configuration: Tweaking the server configuration to handle multiple instances while ensuring proper security and resource utilization is an ongoing challenge.
After numerous rounds of trial and error, I’ve figured it out. I will demonstrate by steps below.
Setting the Stage with Docker Compose
Our adventure begins with a simple yet powerful tool—Docker Compose. This tool allows you to define and run multi-container Docker applications with ease. In our case, we’re using it to manage the deployment of multiple n8n instances.
Here’s a sneak peek into the Docker Compose file:
# Your Docker Compose file
version: '3.7'
services:
n8n1:
# ... (n8n1 configuration)
n8n2:
# ... (n8n2 configuration)
# ... (volumes, networks, etc.)
Each n8n service represents a distinct instance. Simple, right?
Traefik, the Reverse Proxy
# Your Docker Compose file
version: '3.7'
services:
n8n1:
# ... (n8n1 configuration)
# Traefik labels for n8n1
- traefik.enable=true
- traefik.http.routers.n8n1.rule=Host(`n8n1.yourdomain.com`)
# ... (SSL, headers, etc.)
n8n2:
# ... (n8n2 configuration)
# Traefik labels for n8n2
- traefik.enable=true
- traefik.http.routers.n8n2.rule=Host(`n8n2.yourdomain.com`)
# ... (SSL, headers, etc.)
# ... (volumes, networks, etc.)
Traefik ensures that each n8n instance gets its own stage and spotlight.
Crafting the Environment
For each n8n instance, customize through environment variables.
# Sample .env file
DOMAIN_NAME=yourdomain.com
N8N_PORT=5678
N8N_PROTOCOL=https
N8N_ENCRYPTION_KEY=your-super-sercet-key
# ... (other settings)
The Final Docker Compose
version: '3.7'
services:
traefik:
image: 'traefik'
restart: always
command:
- '--api=true'
- '--api.insecure=true'
- '--providers.docker=true'
- '--providers.docker.exposedbydefault=false'
- '--entrypoints.web.address=:80'
- '--entrypoints.web.http.redirections.entryPoint.to=websecure'
- '--entrypoints.web.http.redirections.entrypoint.scheme=https'
- '--entrypoints.websecure.address=:443'
- '--certificatesresolvers.mytlschallenge.acme.tlschallenge=true'
- '--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}'
- '--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json'
ports:
- '80:80'
- '443:443'
- '8080:8080'
volumes:
- traefik_data:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- traefik
n8n1:
container_name: n8n1
image: docker.n8n.io/n8nio/n8n
restart: always
ports:
- '127.0.0.1:5678:5678'
labels:
- traefik.enable=true
- traefik.http.routers.n8n1.rule=Host(`n8n1.${DOMAIN_NAME}`)
- traefik.http.routers.n8n1.tls=true
- traefik.http.routers.n8n1.entrypoints=web,websecure
- traefik.http.routers.n8n1.tls.certresolver=mytlschallenge
- traefik.http.middlewares.n8n1.headers.SSLRedirect=true
- traefik.http.middlewares.n8n1.headers.STSSeconds=315360000
- traefik.http.middlewares.n8n1.headers.browserXSSFilter=true
- traefik.http.middlewares.n8n1.headers.contentTypeNosniff=true
- traefik.http.middlewares.n8n1.headers.forceSTSHeader=true
- traefik.http.middlewares.n8n1.headers.SSLHost=n8n1
- traefik.http.middlewares.n8n1.headers.STSIncludeSubdomains=true
- traefik.http.middlewares.n8n1.headers.STSPreload=true
- traefik.http.routers.n8n1.middlewares=n8n1@docker
environment:
- N8N_HOST=n8n1.${DOMAIN_NAME}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_VERSION_NOTIFICATIONS_ENABLED=true
- NODE_ENV=production
- N8N_METRICS=true
- QUEUE_HEALTH_CHECK_ACTIVE=true
- WEBHOOK_URL=n8n1.${DOMAIN_NAME}/
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
volumes:
- n8n1_data:/home/node/.n8n
networks:
- traefik
n8n2:
container_name: n8n2
image: docker.n8n.io/n8nio/n8n
restart: always
ports:
- '127.0.0.1:8081:5678'
labels:
- traefik.enable=true
- traefik.http.routers.n8n2.rule=Host(`n8n2.${DOMAIN_NAME}`)
- traefik.http.routers.n8n2.tls=true
- traefik.http.routers.n8n2.entrypoints=web,websecure
- traefik.http.routers.n8n2.tls.certresolver=mytlschallenge
- traefik.http.middlewares.n8n2.headers.SSLRedirect=true
- traefik.http.middlewares.n8n2.headers.STSSeconds=315360000
- traefik.http.middlewares.n8n2.headers.browserXSSFilter=true
- traefik.http.middlewares.n8n2.headers.contentTypeNosniff=true
- traefik.http.middlewares.n8n2.headers.forceSTSHeader=true
- traefik.http.middlewares.n8n2.headers.SSLHost=n8n2
- traefik.http.middlewares.n8n2.headers.STSIncludeSubdomains=true
- traefik.http.middlewares.n8n2.headers.STSPreload=true
- traefik.http.routers.n8n2.middlewares=n8n2@docker
environment:
- N8N_HOST=n8n2.${DOMAIN_NAME}
- N8N_PORT=8081
- N8N_PROTOCOL=https
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- N8N_VERSION_NOTIFICATIONS_ENABLED=true
- NODE_ENV=production
- N8N_METRICS=true
- QUEUE_HEALTH_CHECK_ACTIVE=true
- WEBHOOK_URL=n8n2.${DOMAIN_NAME}/
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
volumes:
- n8n2_data:/home/node/.n8n
networks:
- traefik
volumes:
traefik_data:
external: true
n8n1_data:
external: true
n8n2_data:
external: true
networks:
traefik:
external: true
Caution: Before running the Docker Compose command, make sure to execute the commands that create volumes and network:
docker network create traefik
docker volume create traefik_data
docker volume create n8n1_data
docker volume create n8n2_data
The Grand Finale
With the stage set, environment configured, and Traefik donning its director’s hat, it’s time for the grand finale. Launch the show with a simple command:
docker-compose up -d
And voila! Your n8n instances are now dancing elegantly on your server. Access them at https://n8n1.yourdomain.com
and https://n8n2.yourdomain.com
.
Feel free to tweak the script, add more n8n instances, or play with Traefik settings. The .env
file and Docker Compose offer endless possibilities to craft your needs.
Happy orchestrating! 🎉