RU RU

Installing n8n in Docker with HAProxy: A Clear Guide for Beginners

Published on July 2, 2025

Hi friends! Today, we’re diving into the world of automation and deploying n8n — a powerful workflow automation tool. We’ll install it in Docker, and use HAProxy as a reliable reverse proxy to expose it to the internet.

This guide is created especially for beginners. We’ll go step by step, explaining every command so you not only do it, but also understand how everything works.

Why do we even need HAProxy?

You could just expose n8n’s ports to the internet, right? Yes, but that’s unsafe and inconvenient. HAProxy acts as both a guard and a dispatcher:

  1. Security (SSL/TLS): It handles HTTPS requests, encrypting traffic between the user and your server.
  2. Single entry point: All requests to your domain will go to HAProxy, which will decide which application to forward them to. Convenient if you later add other services to the same server.
  3. Flexibility: HAProxy is an incredibly powerful tool. Once you master it, you’ll be able to set up complex rules and load balancing in the future.

What do we need? ✅

  • Server with Ubuntu/Debian: Any virtual or dedicated server will do.
  • Installed Docker and Docker Compose: This is our primary working environment.
  • A domain name: It must be pointed to your server’s IP address (via an A record in DNS settings).
  • SSH access to the server with sudo privileges.

Step 1: Preparing the server and project structure

Let’s first make sure the system is updated and create folders for our project.

  1. Update packages:

    sudo apt update && sudo apt upgrade -y
    
  2. Check Docker versions:

    docker --version
    docker compose version
    

    If the commands return version info — great. If not — install Docker and Compose using official docs.

  3. Create a project directory: We’ll keep everything in one place.

    mkdir ~/n8n-project
    cd ~/n8n-project
    
  4. Create subdirectories: One for n8n data, another for HAProxy config.

    mkdir -p ./n8n-data
    mkdir -p ./haproxy
    

Step 2: Creating the docker-compose.yml file

This is the main file that tells Docker how to run our applications (services). Create a file called docker-compose.yml in the ~/n8n-project folder and add the following code.

⚠️ Important: Replace n8n.YOUR_DOMAIN.com, YOUR_USERNAME, and YOUR_SUPER_PASSWORD with your actual data.

version: '3.8'

services:
  n8n:
    image: n8nio/n8n
    container_name: n8n
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678" # Expose port only to HAProxy on the server
    environment:
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=YOUR_USERNAME # 👈 Replace
      - N8N_BASIC_AUTH_PASSWORD=YOUR_SUPER_PASSWORD # 👈 Replace
      - WEBHOOK_URL=https://n8n.YOUR_DOMAIN.com
      - N8N_HOST=n8n.YOUR_DOMAIN.com # 👈 Replace
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - NODE_ENV=production
    volumes:
      - ./n8n-data:/home/node/.n8n # Mount folder to store n8n data
    networks:
      - n8n_network

  haproxy:
    image: haproxy:2.8 # Use official image
    container_name: haproxy
    restart: unless-stopped
    ports:
      - "80:80"   # For HTTP and certificate issuing
      - "443:443" # For HTTPS
    volumes:
      - ./haproxy:/usr/local/etc/haproxy:ro # :ro means "read-only"
    networks:
      - n8n_network
    depends_on:
      - n8n

networks:
  n8n_network:
    driver: bridge

💡 Key points:

  • ports: - "127.0.0.1:5678:5678": This makes n8n accessible only from within the server — not externally. Only HAProxy can access it.
  • volumes: - ./n8n-data:/home/node/.n8n: This maps n8n-data folder from the host to the container so all workflows and settings persist even if the container is removed or updated.

Step 3: Configuring HAProxy

Now let’s set up our “guard”. Create a file called haproxy.cfg inside ~/n8n-project/haproxy/ and add the following basic configuration.

⚠️ Important: For now, we will not enable SSL/HTTPS. The goal is to first run everything on HTTP, get the certificate, and only then enable encryption.

global
    daemon
    log stdout local0

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5s
    timeout client  50s
    timeout server  50s

frontend http_front
    bind *:80
    acl host_n8n hdr(host) -i n8n.YOUR_DOMAIN.com # 👈 Replace
    use_backend n8n_backend if host_n8n

backend n8n_backend
    server n8n n8n:5678 check

How does this work?

  • frontend http_front: This is the “face” of our proxy. It accepts incoming connections on port 80.
  • acl host_n8n...: Creates a rule (ACL) named host_n8n. It triggers if someone visits n8n.YOUR_DOMAIN.com.
  • use_backend n8n_backend...: If the rule triggers, HAProxy forwards the request to n8n_backend.
  • backend n8n_backend: Describes our application. server n8n n8n:5678 check tells HAProxy: “Send traffic to the n8n container on port 5678.” The name n8n comes from the service name in docker-compose.yml.

Step 4: First launch and getting the SSL certificate

Now that the base is ready, let’s start the containers and get an SSL certificate from Let’s Encrypt using acme.sh. We’ll use the simplest verification method — via HTTP.

  1. Start Docker Compose:

    cd ~/n8n-project
    docker compose up -d
    

    The -d flag runs containers in the background. Check if everything is running:

    docker compose ps
    

    You should see two running containers: n8n and haproxy.

  2. Install acme.sh:

    curl https://get.acme.sh | sh
    source ~/.bashrc
    
  3. Issue the certificate: This command tells Let’s Encrypt to verify your domain. acme.sh creates a temporary file, and Let’s Encrypt will try to access it over HTTP.

    # Tell acme.sh where to place challenge files
    sudo mkdir -p /var/www/acme-challenge 
    sudo chown $USER:$USER /var/www/acme-challenge
    
    # Request certificate
    ~/.acme.sh/acme.sh --issue -d n8n.YOUR_DOMAIN.com --webroot /var/www/acme-challenge/
    
  4. Install the certificate for HAProxy: HAProxy requires key and certificate in one file. acme.sh can do that and set up auto-renewal.

    # Create folder for certificates inside project
    mkdir -p ~/n8n-project/haproxy/certs
    
    # Install certificate with auto-renew command
    ~/.acme.sh/acme.sh --install-cert -d n8n.YOUR_DOMAIN.com \
        --key-file       ~/n8n-project/haproxy/certs/privkey.pem \
        --fullchain-file ~/n8n-project/haproxy/certs/fullchain.pem \
        --reloadcmd      "cat ~/n8n-project/haproxy/certs/fullchain.pem ~/n8n-project/haproxy/certs/privkey.pem > ~/n8n-project/haproxy/certs/custom.pem && docker compose -f ~/n8n-project/docker-compose.yml restart haproxy"
    

    This is the most important command! It not only installs the cert but also sets the reloadcmd, which will be executed every 60 days to auto-renew the cert. It combines the key and cert into one file custom.pem and gracefully restarts HAProxy.


Step 5: Enable HTTPS in HAProxy

Certificate is ready! Now let’s teach HAProxy to use it.

  1. Open ~/n8n-project/haproxy/haproxy.cfg and change it to this:

    global
        daemon
        log stdout local0
        # Add SSL settings
        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
        ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
    
    defaults
        log     global
        mode    http
        option  httplog
        option  dontlognull
        timeout connect 5s
        timeout client  50s
        timeout server  50s
    
    frontend http_https_front
        # Listen on ports 80 and 443
        bind *:80
        bind *:443 ssl crt /usr/local/etc/haproxy/certs/custom.pem
    
        # Automatically redirect HTTP to HTTPS
        http-request redirect scheme https unless { ssl_fc }
    
        # Domain check as before
        acl host_n8n hdr(host) -i n8n.YOUR_DOMAIN.com # 👈 Replace
        use_backend n8n_backend if host_n8n
    
    backend n8n_backend
        # Add header so n8n knows it's behind HTTPS
        http-request set-header X-Forwarded-Proto https if { ssl_fc }
        server n8n n8n:5678 check
    
  2. Restart HAProxy to apply the new config:

    cd ~/n8n-project
    docker compose restart haproxy
    

Step 6: Final check

Open your domain in a browser: https://n8n.YOUR_DOMAIN.com.

You should see:

  1. A padlock in the address bar — meaning SSL is working.
  2. n8n login screen. Enter the username and password you set in docker-compose.yml.

If all that’s working — congratulations! You’ve successfully deployed n8n with secure HTTPS access! 🎉

What’s next? (Maintenance)

  • Updating n8n: To update n8n to the latest version, just run two commands inside ~/n8n-project:

    docker compose pull n8n # Fetch new image version
    docker compose up -d    # Recreate container with new version
    
  • Backups: Your workflows are the most valuable. They’re stored in ~/n8n-project/n8n-data. Back this up regularly.

  • Auto-renew SSL: acme.sh added a cron job automatically. The certificate will renew without your intervention.

I hope this guide was helpful. You’re now ready to automate the world with n8n!

Related Posts

Get in touch

Let's discuss your project and find the right solution