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

  1. Send certificate checks to Certbot: in the frontend add acl is_acme path_beg /.well-known/acme-challenge/ and use_backend certbot if is_acme (put it before the other use_backend lines), with backend certbot pointing to server c1 127.0.0.1:8888.
  2. Request a certificate: certbot certonly --standalone --http-01-port 8888 -d site1.example.com.
  3. 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.
  4. Add bind *:443 ssl crt /etc/haproxy/certs/ to the frontend and reload HAProxy. Repeat the combine step after each renewal (a Certbot --deploy-hook can do it and reload HAProxy).

Common problems

  • 503 Service Unavailable: the backend container is down or listens on another port. Check docker ps and curl -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.cfg to see the line with the error.

Related services

KVM VPS hosting

NVMe SSD and full root access, with Free Basic Managed Support.

Unmanaged dedicated servers

full root access and free IPMI KVM, with Free Basic Managed Support.

Managed dedicated servers

our engineers look after the server for you, from $135 a month.

Was this answer helpful?

Still stuck? Open a ticket