The first hands-on episode: installing HAProxy on Linux, creating a minimal HTTP forwarding configuration, running the service, verifying it with curl, and reading basic logs to confirm traffic is really flowing.

Enough theory. Episode 3 is the first moment you see HAProxy actually working: installing it, writing a minimal configuration, running the service, and verifying that HTTP requests really get forwarded to a backend.
Our scenario is simple but real: two small web servers behind a single HAProxy. After this episode, you'll have your own lab you can use for every practice session in this series.
On Debian and Ubuntu, installation is a single command:
sudo apt update
sudo apt install -y haproxyOn other distributions the command changes accordingly: dnf install haproxy for Fedora/RHEL, pacman -S haproxy for Arch. The version from repositories is often older than the latest release; if you need a newer version, use the official builds or compile from source.
haproxy -vMake sure haproxy -v prints the version and build date. Versions above 2.0 are comfortable for all the material in this series.
If you want to try it without cluttering your system, run a container:
docker run -d --name haproxy-test -p 80:80 \
-v /home/arman/haproxy:/usr/local/etc/haproxy:ro \
haproxy:lts-alpineCreate the configuration directory first, then mount it into the container. This approach is reused in episode 19 for Kubernetes.
Before writing the configuration, prepare two mini web servers as backends. You can use python3 -m http.server on two different ports:
python3 -m http.server 8080 --directory /tmp/web1 &
python3 -m http.server 8081 --directory /tmp/web2 &Create two directories containing an index.html file with different text so it's easy to see which server is serving.
Write the following configuration to /etc/haproxy/haproxy.cfg:
global
log /dev/log local0
maxconn 2048
defaults
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
frontend web_front
bind *:80
default_backend web_back
backend web_back
balance roundrobin
server web1 127.0.0.1:8080 check
server web2 127.0.0.1:8081 checkThe configuration above listens on port 80, then distributes traffic round-robin across the two backends. The check directive in server web1 127.0.0.1:8080 check makes HAProxy monitor server health periodically.
Before starting the service, validate the syntax first:
haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl restart haproxy
sudo systemctl status haproxy --no-pagerhaproxy -c -f /etc/haproxy/haproxy.cfg should print Configuration file is valid. If there's an error, read the line it points to and fix it before restarting.
From the same machine, hit port 80 several times:
curl -s http://localhost/ | head -n 1
curl -s http://localhost/ | head -n 1
curl -s http://localhost/ | head -n 1Because of balance roundrobin, the answer should alternate between backend one and backend two. That's your first visual proof that load balancing is working.
HAProxy's HTTP logs can be read through syslog. Make sure the log directive is in global and option httplog is enabled:
defaults
mode http
option httplog
log globalAfter reloading, new requests will be recorded in /var/log/haproxy.log or via journald depending on the distribution:
sudo journalctl -u haproxy -fEach log line shows the client IP, timestamp, and the frontend-backend/server that handled the request. Getting used to reading these logs early on pays off during troubleshooting in episode 7.
If the service fails to start, the most frequent causes are:
haproxy -c.python3 -m http.server is still running.ss -tlnp | grep :80
ps aux | grep http.serverThe command ss -tlnp | grep :80 confirms HAProxy is really listening on port 80, and ps aux | grep http.server confirms the backends are alive.
Congratulations, you just ran your first HAProxy! From installation, to a minimal configuration, to verification with curl and logs, your lab is now ready for the material ahead.
Key takeaways:
haproxy -c before restarting.balance roundrobin distributes traffic across backends in turn.option httplog and read the logs to understand the traffic flow.In the next episode we'll dive deep into frontend & backend configuration — building complete sections with bind, ACLs, and use_backend, as well as managing several frontends and backends at once in a single configuration file.