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:
- Security (SSL/TLS): It handles HTTPS requests, encrypting traffic between the user and your server.
- 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.
- 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.
Update packages:
sudo apt update && sudo apt upgrade -y
Check Docker versions:
docker --version docker compose version
If the commands return version info — great. If not — install Docker and Compose using official docs.
Create a project directory: We’ll keep everything in one place.
mkdir ~/n8n-project cd ~/n8n-project
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 mapsn8n-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) namedhost_n8n
. It triggers if someone visitsn8n.YOUR_DOMAIN.com
.use_backend n8n_backend...
: If the rule triggers, HAProxy forwards the request ton8n_backend
.backend n8n_backend
: Describes our application.server n8n n8n:5678 check
tells HAProxy: “Send traffic to then8n
container on port 5678.” The namen8n
comes from the service name indocker-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.
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
andhaproxy
.Install acme.sh:
curl https://get.acme.sh | sh source ~/.bashrc
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/
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 filecustom.pem
and gracefully restarts HAProxy.
Step 5: Enable HTTPS in HAProxy
Certificate is ready! Now let’s teach HAProxy to use it.
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
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:
- A padlock in the address bar — meaning SSL is working.
- 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!