Sign inSign up
HAProxy

dhi.io/haproxy

HAProxy

CIS
FIPS
STIG
linux/amd64
linux/arm64

The Reliable, High Performance TCP/HTTP Load Balancer

Prerequisites

All examples in this guide use the public image. If you’ve mirrored the repository for your own use (for example, to your Docker Hub namespace), update your commands to reference the mirrored image instead of the public one.

For example:

  • Public image: dhi.io/<repository>:<tag>
  • Mirrored image: <your-namespace>/dhi-<repository>:<tag>

For the examples, you must first use docker login dhi.io to authenticate to the registry to pull the images.

Refer to the upstream's documentation⁠ on the subject of configuring HAProxy for your needs.

Start a HAProxy image

Run a basic HAProxy container and output the version with the following command. Replace with the image variant you want to run.

docker run dhi.io/haproxy:<tag> -v

Run a HAProxy container with a mounted configuration directory:

docker run -v /path/to/haproxy:/usr/local/etc/haproxy:ro dhi.io/haproxy:<tag> -f /usr/local/etc/haproxy

Using Docker Compose:

services:
  haproxy:
    image: dhi.io/haproxy:<tag>
    volumes:
      - ./haproxy:/usr/local/etc/haproxy:ro
    ports:
      - "8080:8080"
      - "8443:8443"
    command: ["-f", "/usr/local/etc/haproxy"]

Common HAProxy use cases

Basic HTTP load balancer

Create a simple load balancer configuration in haproxy.cfg:

global
    maxconn 256

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend http-in
    bind *:8080
    default_backend servers

backend servers
    server server1 192.168.1.10:80 check
    server server2 192.168.1.11:80 check

Mount and run with:

docker run -v $(pwd)/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro \
  -p 8080:8080 dhi.io/haproxy:<tag> \
  -f /usr/local/etc/haproxy
Load balancer with SSL termination

Configure SSL termination in haproxy.cfg:

global
    maxconn 256

defaults
    mode http
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend https-in
    bind *:8443 ssl crt /usr/local/etc/haproxy/certs/server.pem
    default_backend servers

backend servers
    server server1 192.168.1.10:80 check
    server server2 192.168.1.11:80 check

Mount certificates and configuration:

docker run -v $(pwd)/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro \
  -v $(pwd)/certs:/usr/local/etc/haproxy/certs:ro \
  -p 8443:8443 dhi.io/haproxy:<tag> \
  -f /usr/local/etc/haproxy
TCP load balancer for databases

Configure TCP mode for database load balancing:

global
    maxconn 256

defaults
    mode tcp
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend database-in
    bind *:5432
    default_backend db-servers

backend db-servers
    balance roundrobin
    server db1 192.168.1.20:5432 check
    server db2 192.168.1.21:5432 check

Docker Official Images vs. Docker Hardened Images

Key differences for HAProxy:

FeatureDocker Official HAProxyDocker Hardened HAProxy
SecurityStandard base with common utilitiesMinimal, hardened base with security patches
Shell accessFull shell (bash/sh) availableNo shell in runtime variants
Package managerapt/apk availableNo package manager in runtime variants
Attack surfaceLarger due to additional utilitiesMinimal, only essential components
DebuggingTraditional shell debuggingUse Docker Debug or Image Mount for troubleshooting

Image variants

Docker Hardened Images come in different variants depending on their intended use:

  • Runtime variants are designed to run HAProxy in production. These images:

    • Run as the nonroot user
    • Do not include a shell or package manager
    • Contain only the minimal set of libraries needed to run HAProxy
  • Build-time variants typically include dev in the variant name and are intended for building custom HAProxy configurations or plugins. These images:

    • Run as the root user
    • Include a shell and package manager
    • Can be used to compile HAProxy modules or validate configurations
  • FIPS variants include fips in the variant name and tag. They come in both runtime and build-time variants. These variants use cryptographic modules that have been validated under FIPS 140, a U.S. government standard for secure cryptographic operations. For example, usage of MD5 fails in FIPS variants.

Migrate to a Docker Hardened Image

HAProxy-specific migration requirements

When migrating from Docker Official Images to Docker Hardened Images for HAProxy, consider the following:

Port binding restrictions

The nonroot user cannot bind to privileged ports (below 1024). Update your HAProxy configuration to use ports 1025 and above inside the container:

# Change from:
bind *:80
bind *:443

# To:
bind *:8080
bind *:8443

Use Docker's port mapping to expose these on privileged ports on the host:

docker run -p 80:8080 -p 443:8443 dhi.io/haproxy:<tag>
Configuration file permissions

Ensure configuration files are readable by the nonroot user. Mount configurations with appropriate permissions:

chmod 644 haproxy.cfg
docker run -v $(pwd)/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro \
  dhi.io/haproxy:<tag>
Entry point differences

Verify the entry point behavior matches your expectations. Use docker inspect to check:

docker inspect dhi.io/haproxy:<tag>
Stats socket location

If using the stats socket, ensure it's writable by the nonroot user. Consider using a mounted volume:

docker run -v haproxy-socket:/var/run/haproxy \
  dhi.io/haproxy:<tag>

Troubleshoot migration

General Debugging

The hardened images intended for runtime don't contain a shell nor any tools for debugging. The recommended method for debugging applications built with Docker Hardened Images is to use Docker Debug to attach to these containers. Docker Debug provides a shell, common debugging tools, and lets you install other tools in an ephemeral, writable layer that only exists during the debugging session.

docker debug dhi.io/haproxy:<tag>
HAProxy won't start - permission denied on port binding

Symptom: HAProxy fails to start with a "permission denied" error when binding to ports below 1024.

Solution: Update your HAProxy configuration to use unprivileged ports (1025 and above) and use Docker port mapping to expose privileged ports on the host.

Configuration file not found or not readable

Symptom: HAProxy reports that it cannot read the configuration file.

Solution: Verify that:

  • The configuration file exists in the mounted location
  • The file has appropriate read permissions (644 or similar)
  • The mount path in the Docker command matches the path specified in the HAProxy command
Stats socket permission denied

Symptom: HAProxy cannot create or write to the stats socket.

Solution: Create a named volume or bind mount with appropriate permissions for the nonroot user to write the socket file, typically in /var/run/haproxy.

Cannot debug running container

Symptom: Unable to exec into the container because there's no shell.

Solution: Use Docker Debug to attach an ephemeral debugging session to the container with a shell and debugging tools.

Other

Configuration validation

To validate your HAProxy configuration before deploying, use a dev variant image:

docker run --rm -v $(pwd)/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro \
  dhi.io/haproxy:<tag>-dev \
  -c -f /usr/local/etc/haproxy/haproxy.cfg
Additional documentation

For detailed HAProxy configuration and tuning guidance, refer to the official HAProxy documentation.