Updated: 28 September 2026 · Applies to: Ubuntu 24.04, Debian 12, AlmaLinux 9 and Rocky Linux 9
HAProxy can route traffic for several domains to different containers on one server.
Install Docker Engine (official repository)
Ubuntu 24.04 / Debian 12:
apt install ca-certificates curl install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc tee /etc/apt/sources.list.d/docker.sources <<EOF Types: deb URIs: https://download.docker.com/linux/ubuntu Suites: $(. /etc/os-release && echo "$VERSION_CODENAME") Components: stable Architectures: $(dpkg --print-architecture) Signed-By: /etc/apt/keyrings/docker.asc EOF apt update apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
On Debian, use https://download.docker.com/linux/debian in both the key URL and URIs.
AlmaLinux 9 / Rocky Linux 9:
dnf install dnf-plugins-core dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo dnf install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin systemctl enable --now docker
Test with docker run hello-world. Compose is available as docker compose.
Run the sites
docker run -d --name site1 -p 127.0.0.1:8081:80 nginx docker run -d --name site2 -p 127.0.0.1:8082:80 httpd
Install and configure HAProxy
apt install haproxy # dnf install haproxy on AlmaLinux/Rocky
In /etc/haproxy/haproxy.cfg:
frontend http_in
bind *:80
acl is_site1 hdr(host) -i site1.example.com
acl is_site2 hdr(host) -i site2.example.com
use_backend site1 if is_site1
use_backend site2 if is_site2
backend site1
server s1 127.0.0.1:8081 check
backend site2
server s2 127.0.0.1:8082 check
Check with haproxy -c -f /etc/haproxy/haproxy.cfg and restart. For HTTPS, bind *:443 ssl crt /etc/haproxy/certs/ with combined certificate files (for example from Certbot).
Note: On AlmaLinux 9 / Rocky Linux 9 allow HAProxy to connect to the ports: setsebool -P haproxy_connect_any 1.
How it works
HAProxy is the only service listening on ports 80 and 443. It reads the host name of each request and forwards it to the matching container, which listens only on 127.0.0.1. Add a site by starting another container on a new local port and adding an ACL and a backend.
HTTPS with Let's Encrypt
- Send certificate checks to Certbot: in the frontend add
acl is_acme path_beg /.well-known/acme-challenge/anduse_backend certbot if is_acme(put it before the otheruse_backendlines), withbackend certbotpointing toserver c1 127.0.0.1:8888. - Request a certificate:
certbot certonly --standalone --http-01-port 8888 -d site1.example.com. - Combine it for HAProxy:
cat /etc/letsencrypt/live/site1.example.com/fullchain.pem /etc/letsencrypt/live/site1.example.com/privkey.pem > /etc/haproxy/certs/site1.example.com.pem. - Add
bind *:443 ssl crt /etc/haproxy/certs/to the frontend and reload HAProxy. Repeat the combine step after each renewal (a Certbot--deploy-hookcan do it and reload HAProxy).
Common problems
- 503 Service Unavailable: the backend container is down or listens on another port. Check
docker psandcurl -I http://127.0.0.1:8081. - Wrong site shown: the ACL host name does not match.
hdr(host)must match the exact domain; add a second ACL for www. - HAProxy does not start: run
haproxy -c -f /etc/haproxy/haproxy.cfgto see the line with the error.