Reloading HAProxy configuration without dropping connections is surprisingly easy, but most people overcomplicate it by thinking they need to restart the entire process.

Here’s HAProxy in action, serving traffic. We’ll simulate a configuration change and then reload it live.

# Simulate a backend server going down
echo "DOWN" | sudo tee /etc/haproxy/backend_status.txt

# HAProxy config snippet that checks the status file
backend my_backend
  server s1 192.168.1.10:80 check port 80 inter 2s fall 3 rise 2
  server s2 192.168.1.11:80 check port 80 inter 2s fall 3 rise 2

# HAProxy config snippet that uses the status file
backend my_backend
  server s1 192.168.1.10:80 check port 80 inter 2s fall 3 rise 2
  server s2 192.168.1.11:80 check port 80 inter 2s fall 3 rise 2
  option httpchk GET /health
  http-check expect status 200
  http-check send-state "" if { hdr(Host) -i example.com }
  # This next line is where we'll dynamically change backend availability
  http-request set-header X-Backend-Status %[file[/etc/haproxy/backend_status.txt]]

# Now, let's trigger a reload.
# First, check the current HAProxy process ID.
pgrep -f haproxy
# Let's say the PID is 12345

# Send the HUP signal to reload the configuration.
sudo kill -HUP 12345

# After a moment, check the HAProxy stats page (if configured)
# or observe traffic to see that the configuration has been applied.

HAProxy is designed for high availability, and its core principle for configuration reloads is to leverage signals. When you send the HUP signal to the HAProxy master process, it doesn’t kill and restart the process. Instead, it tells the master process to re-read its configuration file. The master process then spawns new worker processes with the updated configuration. Crucially, the old worker processes continue to serve existing connections until they have finished processing them or the new workers are fully ready. New connections are then directed to the new workers. This graceful handover ensures zero downtime for your services.

The primary problem HAProxy solves is acting as a transparent, high-performance TCP/HTTP load balancer and proxy. It can distribute traffic across multiple backend servers, perform health checks, terminate SSL, and much more, all while maintaining excellent throughput and low latency. Its configuration reload mechanism is a key feature for achieving continuous service availability.

The mental model to hold onto is that HAProxy runs as a master process that manages worker processes. When a HUP signal is received, the master process doesn’t terminate itself. It initiates a rolling restart of its workers. It reads the new configuration, starts new workers based on that config, and then gracefully shuts down the old workers after they’ve finished their current tasks. This is why existing connections are not dropped. The master process itself only truly exits if it receives a TERM or QUIT signal, or if it’s killed directly.

Here’s a breakdown of how it works internally:

  1. Master Process: The main HAProxy process. It’s responsible for managing worker processes and handling signals.
  2. Worker Processes: These are the processes that actually accept incoming connections and forward them to backend servers. They run the bulk of the traffic.
  3. Signal Handling: When the master process receives a HUP signal (from kill -HUP <pid>), it triggers a re-configuration.
  4. New Workers Spawned: The master reads the updated haproxy.cfg. It then starts new worker processes, loading the new configuration into them.
  5. Graceful Shutdown: Once the new workers are ready and accepting traffic, the master process signals the old worker processes to start shutting down. These old workers finish processing their current requests and then exit.
  6. Connection Continuity: Because old workers only exit after finishing current tasks, and new workers are already handling new requests, there’s no interruption for clients. Existing connections on old workers are allowed to complete.

The most surprising aspect of HAProxy’s reload mechanism is that the HUP signal doesn’t just re-read the config; it orchestrates a complete, zero-downtime replacement of the active worker processes. This is a sophisticated form of rolling update managed by the master process itself, rather than requiring external orchestration tools to manage the restart lifecycle. It’s a built-in feature for high availability.

The next challenge you’ll likely encounter is how to manage HAProxy’s state, such as session persistence or backend server status, across reloads, especially when the state might be stored externally.

Want structured learning?

Take the full Haproxy course →