For the complete documentation index, see llms.txt. This page is also available as Markdown.

High Availability Proxy (HAProxy)

Introduction

HAProxy is used as a load balancer and reverse proxy in front of the Helmut4 servers. In this setup, HAProxy provides a single entry point for users and services, handles HTTPS traffic, forwards requests to the available Helmut4 backend servers, and performs health checks to make sure traffic is only routed to reachable systems.

This guide describes how HAProxy can be added to an existing Docker Swarm environment and used together with Helmut4. The setup also uses Keepalived to provide a virtual IP address, which allows clients to connect to a stable address even if the active Docker manager node changes.


Portainer Stack Example

Keep alive & HAProxy (Compose for Stack)
networks:
  host:
    external: true
    name: host

configs:
  haproxy.conf:
    external: true
  helmut4_server.pem:
    external: true

services:
  keepalived:
    restart: always
    image: ghcr.io/lhns/keepalived-swarm:0.2.7
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /usr/bin/docker:/usr/bin/docker:ro
    networks:
      - host
    deploy:
      mode: global
      placement:
        constraints:
          - node.role == manager
    environment:
      KEEPALIVED_VIRTUAL_IPS: 10.123.117.89

  haproxy:
    image: haproxytech/haproxy-alpine:3.3
    restart: always
    configs:
      - source: haproxy.conf
        target: /usr/local/etc/haproxy/haproxy.cfg
      - source: helmut4_server.pem
        target: /etc/ssl/certs/server.pem
    networks:
      - host
    depends_on:
      - keepalived
    deploy:
      mode: global
      placement:
        constraints:
          - node.role == manager

The latest versions of the dedicated images can be found here:


HAProxy Configuration Example

haproxy.conf
global
    log stdout format raw local0 info
    ca-base /etc/ssl/certs
    crt-base /etc/ssl/private
    ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
    ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
    ssl-server-verify none
    h1-case-adjust content-length Content-Length
    h1-case-adjust version Version
    h1-case-adjust content-type Content-Type

defaults
    mode http
    log global
    timeout client 1000s
    timeout connect 5000s
    timeout server 5000s
    timeout http-request 1000s

frontend stats
    bind *:8083
    mode http
    option http-keep-alive
    stats enable
    stats uri /
    stats refresh 5s
    stats auth admin:AdMiN123
    stats admin if TRUE
    stats show-legends
    stats show-modules
    stats show-node
    stats realm HAProxy\ Statistics

frontend helmut_frontend_ssl
    mode http
    bind *:80
    bind *:443 ssl crt /etc/ssl/certs/server.pem
    option http-keep-alive

    http-request redirect scheme https unless { ssl_fc }

    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    http-request set-header X-Forwarded-Proto http unless { ssl_fc }
    http-request set-header X-Forwarded-For %[src]
    http-request set-header X-Real-IP %[src]

    default_backend helmutservers_ssl

backend helmutservers_ssl
    mode http
    balance roundrobin
    default-server inter 5s fall 3 rise 2

    option httpchk GET /v1/fx/version
    http-check expect rstring ^.*version.*:.*4.*$

    timeout tunnel 60s

    stick-table type ip size 1m expire 30m
    stick on src

    server helmut1 helmut-server-01.example.com:8443 ssl check
    server helmut2 helmut-server-02.example.com:8443 ssl check
    server helmut3 helmut-server-03.example.com:8443 ssl check

Please replace the placeholder DNS name helmut-server-01.example.com:8443 with the actual DNS name and external port used in your environment.


Certificate File: PEM

For HTTPS, HAProxy requires a certificate file that contains both the public certificate and the matching private key.

In this setup, only the following certificate file is needed:

The .pem file is used directly by HAProxy for SSL/TLS termination. It must contain both the certificate and the private key in the same file.

HAProxy references this file in the bind statement:

In the Docker Compose file, the PEM file is mounted into the HAProxy container as a Docker config:

The file mounted at /etc/ssl/certs/server.pem is the certificate file HAProxy uses for HTTPS connections.

A valid PEM file should contain the certificate and private key blocks, for example:

or:


How the Setup Works

Keepalived provides the virtual IP address:

Clients connect to this virtual IP address instead of connecting directly to a specific Helmut4 server.

HAProxy listens on ports 80 and 443:

HTTP traffic on port 80 is automatically redirected to HTTPS:

HAProxy then forwards the request to one of the configured Helmut4 backend servers:

Helmut4 - Exposed Taefik Ports

It is necessary to change the exposed ports of the Helmut4 Traefik container to different values, because HAProxy itself uses ports 80 and 443.

This change is only required if HAProxy is running on the same host as Helmut4.

A step-by-step guide for changing the ports can be found here: Change HTTP/HTTPS Ports

The backend servers are checked using the Helmut4 version endpoint:

Only servers that return the expected response are considered healthy and receive traffic.


Session Stickiness

The configuration uses source-IP-based stickiness:

This means that requests from the same client IP are routed to the same backend server for 30 minutes. This can be useful when backend-side session behavior or temporary state should stay on the same Helmut4 server during a user session.


HAProxy Statistics Page

The configuration also enables the HAProxy statistics interface on port 8083:

This page can be used to monitor backend server status, active connections, health checks, and traffic distribution.

For production environments, the username and password should be changed and access to the statistics page should be restricted.

Last updated