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! 🎉