Cum să instalezi HAProxy cu Docker Compose pe Ubuntu VPS
One web service on a VPS is easy to expose directly — until you want one clean public front door, the freedom to swap the backend later, or a safer way to stop sending traffic to something broken. That is the point where a proxy stops feeling like “something for big infrastructure teams” and starts feeling practical.

HAProxy fits that role well. Think of it as the traffic manager sitting in front of your application: requests hit HAProxy first, and HAProxy decides where they should go next. You do not need a large cluster to benefit from that. Even on one Ubuntu 24.04 VPS, it gives you a cleaner edge between the internet and the service you are actually running.
This guide keeps the first deployment intentionally structured: one Ubuntu 24.04 VPS, Docker Compose, one HAProxy container, one demo backend, and proof that routing really works.
De ce HAProxy este Important Înainte să Ai Nevoie de El
Imaginează-ți un mic VPS care rulează o aplicație perfect astazi. Răspunde pe un port, site-ul se încarcă, și totul arată bine. Fricțiunea apare atunci când vrei un punct de intrare public stabil, opțiunea de a înlocui backend-ul mai târziu fără a schimba adresa publică, sau un strat frontal care poate opri trimiterea de trafic către un serviciu defect. Expunerea directă a aplicației începe să se simtă fragilă surprinzător de repede.

Aceste cerințe indică toate același strat lipsă: un punct de intrare controlat între internet și aplicația ta. HAProxy oferă acel strat. Clienții se conectează mai întâi la HAProxy, iar HAProxy decide unde merge fiecare cerere în continuare.
Acea separare este utilă chiar și înainte să ai mai multe servere. Îți oferă o margine publică mai curată acum și o cale mai sigură pentru schimbări ulterioare, cum ar fi înlocuirea backend-ului, rutarea conștientă de sănătate, și HTTPS. Restul ghidului arată acel model în forma sa cea mai simplă de lucru și îl verifică cu o cale de cerere reală.
Termeni HAProxy Rapizi Care Fac Restul Acestui Ghid Mai Ușor

Trebuie doar un set mic de vocabular pentru a urma cu încredere o primă implementare HAProxy. Tabelul de mai jos acoperă termenii care contează în acest ghid.
| Termen | Semnificație în limba română simplă |
|---|---|
| 🌐 reverse proxy | Un serviciu orientat spre exterior care primește cererile mai întâi și le transmite unui alt serviciu intern. |
| ⚖️ load balancer | Un strat frontal care poate distribui cererile pe mai mult de o țintă backend. |
| 🚪 frontend | Locul unde clienții se conectează la HAProxy. |
| 🧩 backend | Serviciul sau serverul către care HAProxy trimite cererea în continuare. |
| ❤️ health check | O modalitate pentru HAProxy de a observa dacă un backend ar trebui să continue să primească trafic. |
| 🐳 image | Un șablon de aplicație ambalat folosit pentru a crea containere. |
| 📦 container | O instanță în execuție a unei imagini. |
Pentru acest ghid, reverse proxy este primul model mental pe care trebuie să-l ții în minte. HAProxy stă în fața a ceva altceva și controlează transferul. Load balancing este capacitatea extinsă care devine utilă atunci când adaugi mai târziu mai multe servere backend.
Cei doi termeni care contează cel mai mult odată ce deschizi configurația sunt frontend și backend. Frontend-ul este locul unde sosește clientul. Backend-ul este locul unde HAProxy trimite cererea în continuare. Un health check contează pentru că permite HAProxy să observe când o țintă ar trebui să înceteze să primească trafic.
Ce este bun HAProxy — și ce omite intenționat acest ghid

Dacă vă imaginați stiva dvs. ca o clădire de birouri, HAProxy este recepția: traficul ajunge acolo mai întâi, este direcționat către camera potrivită și nu mai este trimis către o cameră care este clar indisponibilă.
În acest ghid, aceasta se traduce în trei joburi relevante pentru începători:
- accepta cererile HTTP primite
- le transmite la backend-ul demo
- monitoriza dacă acel backend este suficient de sănătos pentru a continua să primească trafic
Aceasta este deja utilă cu un backend deoarece vă oferă o margine publică controlată în fața aplicației.
Mai târziu, același model se scalează curat. Puteți înlocui backend-ul, adăuga mai multe backend-uri, introduce HTTPS, sau lăsa HAProxy să distribuie traficul pe mai multe ținte în loc de doar una. Pentru a menține prima trecere didactică, acest ghid rămâne în modul HTTP și omite intenționat terminarea TLS, ACL-uri, limitarea ratei, tabele stick și perechi HA. Toate acestea sunt subiecte reale HAProxy. Pur și simplu nu sunt punctul de plecare potrivit pentru o primă implementare funcțională.
Ce construiești și ce ai nevoie mai întâi

Înainte de a crea fișiere, ajută să vezi forma finală a stack-ului. Implementarea din acest ghid arată așa:
Client browser or curl
|
v
HAProxy frontend (:80)
|
v
demo backend service (demo:5678)
Optional local-only validation:
HAProxy stats frontend (127.0.0.1:8404/stats)Docker Compose este calea principală aici pentru că ține prima instalare reproductibilă, vizibilă și ușor de editat. În loc să construiești o imagine personalizată în prima zi, ții configurația HAProxy pe gazdă, o montezi în container și pornești întregul stack dintr-un singur fișier. Pe un VPS Ubuntu auto-gestionat — de exemplu, un VPS AlexHost — aceasta se potrivește perfect pentru că aspectul rămâne ușor de inspectat.
💡 Sfat: Acest ghid folosește Docker Compose plus un haproxy.cfg bind-mounted în mod intenționat. Este calea cea mai transparentă pentru prima instalare pentru că poți edita configurația proxy direct fără a adăuga un pas de construire a imaginii.
Înainte de a începe, asigură-te că ai aceste elemente de bază în loc:
- Ubuntu 24.04 VPS
- Docker Engine instalat
- Docker Compose v2 disponibil prin docker compose
- Acces la terminal și permisiune de a rula Docker
- Portul 80 disponibil pe gazdă
- HTTP inbound permis dacă folosești UFW sau reguli de firewall pe partea furnizorului
Mai întâi, verifică versiunea Ubuntu
lsb_release -a
Apoi, confirmă că Docker și Compose modern sunt disponibili:
docker --version
docker compose version
Dacă ambele comenzi returnează informații despre versiune, partea runtime a containerului este gata și poți rămâne concentrat pe HAProxy în loc să faci o detură în instalarea Docker.
Apoi, asigură-te că portul 80 nu este deja în uz, apoi verifică dacă UFW este activ și dacă HTTP este deja permis:
sudo ss -tlnp | grep -E ':(80)s' || true
sudo ufw status
sudo ufw allow 80/tcp
✏️ NOTĂ: Nicio ieșire din verificarea ss de obicei înseamnă că portul 80 este liber. Dacă vezi nginx, apache2, caddy sau alt serviciu deja ascultând acolo, remediază asta mai întâi. Este un pas de preflight de zece secunde care economisește multă confuzie mai târziu.
În exemplul de mai sus, sudo ufw status arată Status: active, și 80/tcp este deja prezent în lista de permisiuni. De aceea sudo ufw allow 80/tcp returnează Skipping adding existing rule în loc să adauge una nouă. Această ieșire este normală și pur și simplu înseamnă că regula firewall-ului era deja în loc.
Creați Folderul Proiectului și Fișierul Compose
Începeți prin crearea unui mic folder de proiect pentru cele două fișiere necesare acestei prime implementări:
mkdir -p ~/haproxy-docker
cd ~/haproxy-docker
După aceea, structura ar trebui să fie cât mai mică posibil:
~/haproxy-docker/
├── compose.yaml
└── haproxy.cfgAcum creați compose.yaml și utilizați exact acest conținut:
services:
demo:
image: hashicorp/http-echo:1.0
command: ["-listen=:5678", "-text=Hello from the HAProxy demo backend"]
restart: unless-stopped
haproxy:
image: haproxy:3.4.1
depends_on:
- demo
ports:
- "80:80"
- "127.0.0.1:8404:8404"
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
sysctls:
net.ipv4.ip_unprivileged_port_start: "0"
restart: unless-stoppedAcest fișier conectează containerele între ele, dar nu definește încă logica cererilor HAProxy. El spune Docker-ului care imagini să ruleze, care porturi să publice și de unde va fi montat configul HAProxy de pe gazdă.
Următoarele setări sunt cele care contează cel mai mult pentru o implementare curată și de început:
| Setarea Compose | De ce se află aici |
|---|---|
| hashicorp/http-echo:1.0 | Vă oferă un backend demo minuscul și previzibil fără a trebui să învățați un al doilea server web în același timp. |
| haproxy:3.4.1 | Utilizează o etichetă stabilă fixată în loc de latest, ceea ce menține ghidul mai puțin fragil în timp. |
| depends_on | Pornește serviciul demo înainte de HAProxy, ceea ce este util pentru ordinea primei rulări. |
| 80:80 | Publică ascultătorul HTTP principal pe portul web standard pe care cititorii îl așteaptă. |
| 127.0.0.1:8404:8404 | Ține pagina de statistici disponibilă pentru validarea locală fără a o expune public în mod implicit. |
| ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro | Montează fișierul de configurare vizibil de pe gazdă în imaginea oficială HAProxy ca doar pentru citire. |
| sysctls cu net.ipv4.ip_unprivileged_port_start: “0” | Permite containerului HAProxy non-root să se lege la porturi joase cum ar fi 80. |
| restart: unless-stopped | Vă oferă o valoare implicită practică VPS: reporniți după eșec sau reboot, dar respectați o oprire manuală intenționată. |
Un detaliu mai important conteaza aici: nu există o rețea Docker personalizată în acest fișier deoarece Docker Compose creează o rețea implicită automat. Aceasta vă oferă DNS cu nume de serviciu în cadrul proiectului, motiv pentru care HAProxy va putea ajunge la backend ca demo:5678 fără cablare suplimentară.
⚠️ Avertisment: Portul 80 este un port privilegiat, deci linia sysctls nu este decorativă. Schimbarea mapării gazdei la 8080:80 nu elimină cerința portului privilegiat în interiorul containerului dacă HAProxy se leagă în continuare la :80 intern.
Scrieți și validați un minimal haproxy.cfg
Cu cablajul containerului în loc, HAProxy are încă nevoie de instrucțiuni pentru locul unde sosește traficul, unde ar trebui să meargă și cum este verificată sănătatea backend-ului. Creați haproxy.cfg în continuare:
global
log stdout format raw local0
defaults
mode http
timeout connect 5s
timeout client 30s
timeout server 30s
frontend http
bind :80
default_backend demo_backend
backend demo_backend
balance roundrobin
server demo1 demo:5678 check
frontend stats
bind :8404
stats enable
stats refresh 10s
stats uri /statsAceasta este o configurație minimală, dar nu este una de unică folosință. log stdout format raw local0 este alegerea de logging prietenoasă cu containerele, deoarece Docker poate expune stdout ușor, iar mode http în defaults ține întregul exemplu în modul HTTP, astfel încât comportamentul ascultătorului și backend-ului rămân consecvenți și lizibili.
✏️ NOTĂ: Un detaliu merită să fie evidențiat înainte de descompunerea secțiunii: balance roundrobin este setat explicit deoarece versiunile mai noi de HAProxy au schimbat algoritmul implicit al backend-ului la random, iar roundrobin este mai ușor de predat în mod previzibil la o primă trecere.
Iată descompunerea în limba engleză simplă a fiecărei secțiuni:
| Secțiune | Linii cheie | Ce face |
|---|---|---|
| global | log stdout format raw local0 | Trimite jurnalele la stdout, astfel încât logging-ul Docker rămâne simplu. |
| defaults | mode http, timeouts | Stabilește comportamentul HTTP de bază și valori timeout sensate. |
| frontend http | bind :80, default_backend demo_backend | Creează ascultătorul public și îl conectează la definiția backend-ului. |
| backend demo_backend | balance roundrobin, server demo1 demo:5678 check | Spune HAProxy ce serviciu să folosească și să monitorizeze sănătatea acesteia. |
| frontend stats | bind :8404, stats enable, stats uri /stats | Adaugă o pagină de validare locală opțională, astfel încât să puteți vedea starea runtime mai târziu. |
Puteți observa un lucru lipsă: option forwardfor. Această omisiune este intenționată în calea de bază. Păstrarea IP-ului original al clientului este utilă mai târziu, dar această primă implementare este despre a dovedi rutarea și sănătatea backend-ului, nu despre a preda comportamentul header-ului cu un container demo care nu face acel semnal deosebit de valoros.
💡 Sfat: Validați întotdeauna configurația HAProxy înainte de a porni stiva completă. Deoarece această configurație se referă la backend prin numele serviciului Compose (demo), porniți mai întâi acel backend, astfel încât HAProxy să îl poată rezolva în timpul validării.
Executați validarea din același director de proiect:
docker compose up -d demo
docker compose run --rm --no-deps haproxy haproxy -V -c -f /usr/local/etc/haproxy/haproxy.cfg
Dacă a doua comandă se termină cu Configuration file is valid, ați dovedit deja că HAProxy poate analiza fișierul corect și rezolva ținta backend-ului înainte ca orice ascultător live să pornească.
Porniți Stack-ul și Dovediți că Proxy-ul Funcționează
Odată ce configurația este validată, porniți stack-ul în modul detașat:
Deoarece pasul de validare a deja pornit demo, această comandă aduce în principal HAProxy și reconciliază stack-ul complet cu două servicii:
docker compose up -d
Apoi verificați dacă ambele containere sunt active:
docker compose ps
Acea vizualizare a proceselor este doar primul punct de control. Confirmă că Docker a pornit containerele, dar nu încă că HAProxy direcționează cu succes traficul către backend. Următoarea cerere verifică calea reală a datelor.
Acum rulați testul de rutare actual din VPS-ul însuși:
curl -i http://127.0.0.1
Semnalul de succes este HTTP/1.1 200 OK plus corpul răspunsului care conține Hello from the HAProxy demo backend. Unele compilări http-echo înfășoară acel text într-un răspuns HTML mic, deci concentrați-vă pe fraza din corp mai mult decât pe formatarea exactă.
Dacă doriți o dovadă la nivel de browser, deschideți http://YOUR_SERVER_IP de pe o altă mașină.

Pentru o a doua suprafață de validare, verificați pagina de statistici doar pentru utilizare locală din VPS:
curl http://127.0.0.1:8404/statsPe pagina de statistici, semnalele cele mai utile sunt un frontend numit http, un backend numit demo_backend, un rând de server numit demo1, starea afișată ca UP, și de obicei o valoare de ultimă verificare cum ar fi L4OK in 0ms. De asemenea, rețineți o mică nuanță Docker: depends_on cu sintaxă scurtă controlează ordinea de pornire, dar nu așteaptă ca un serviciu să devină sănătos. Dacă primul curl eșuează o dată chiar după pornire, așteptați câteva secunde și încercați din nou înainte de a presupune că configurația este greșită.
Diferența dintre starea procesului și succesul real este mai ușor de ținut minte sub formă de tabel:
| Stare | Ce vă spune |
|---|---|
| Containerele sunt în execuție | Docker a pornit procesele. |
| curl -i http://127.0.0.1 returnează 200 OK și fraza demo | HAProxy direcționează cu adevărat traficul către backend. |
| Pagina de statistici arată demo1 ca UP | HAProxy vede backend-ul ca fiind sănătos. |
Greșeli comune la prima rulare și remedii rapide

Dacă configurarea nu funcționează imediat, rezistați tentației de a rescrie ambele fișiere deodată. Majoritatea eșecurilor la prima rulare pe acest stack sunt previzibile, și devin mult mai ușor de remediat când schimbați o variabilă la un moment dat.
Utilizați această matrice ca strat de diagnosticare rapidă:
HAProxy se închide imediat
Cauza probabilă: haproxy.cfg lipsește.
Remediu rapid: Asigurați-vă că haproxy.cfg există lângă compose.yaml.
De ce se întâmplă: Imaginea oficială nu vine cu o configurație gata de utilizare.
Eroarea spune că nu poate deschide /usr/local/etc/haproxy/haproxy.cfg
Cauza probabilă: Cale de bind-mount greșită.
Remediu rapid: Verificați ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro exact.
De ce se întâmplă: HAProxy nu poate porni fără un fișier de configurație valid.
Eroarea spune Permission denied pe portul 80
Cauza probabilă: Problemă de legare a portului privilegiat.
Remediu rapid: Păstrați net.ipv4.ip_unprivileged_port_start: “0” în Compose, sau mutați atât HAProxy cât și portul publicat la 8080.
De ce se întâmplă: Containerul rulează ca utilizatorul non-root haproxy.
Ați schimbat maparea la 8080:80 și încă obțineți o eroare de legare
Cauza probabilă: HAProxy se leagă în continuare la :80 în interiorul containerului.
Remediu rapid: Schimbați atât maparea gazdei cât și linia internă bind dacă vă depărtați de portul 80.
De ce se întâmplă: Regula portului privilegiat se aplică și în interiorul containerului.
Portul 80 este deja în uz
Cauza probabilă: Un alt serviciu deține portul gazdei.
Remediu rapid: Re-rulați verificarea ss și opriți sau mutați serviciul conflictual.
De ce se întâmplă: Doar un proces poate asculta pe același port al gazdei.
Verificarea sintaxei raportează unknown keyword sau erori specifice liniei
Cauza probabilă: Greșeală de tastare în configurația HAProxy.
Remediu rapid: Re-rulați verificarea sintaxei și corectați linia exactă pe care o raportează.
De ce se întâmplă: Parserul HAProxy este strict, ceea ce este util odată ce îl utilizați intenționat.
Containerele sunt active, dar curl nu returnează răspunsul demo
Cauza probabilă: Calea de rutare este greșită.
Remediu rapid: Re-verificați default_backend demo_backend, server demo1 demo:5678 check, și numele serviciului demo.
De ce se întâmplă: Un container activ nu este dovada unei căi frontend-to-backend corecte.
curl local funcționează dar site-ul este inaccesibil din exterior
Cauza probabilă: Firewall sau regulă de securitate de la furnizor.
Remediu rapid: Deschideți portul 80 în UFW și orice firewall de pe partea furnizorului.
De ce se întâmplă: Publicarea locală poate funcționa chiar și atunci când accesul public este încă blocat.
⚠️ Avertisment: Schimbați un lucru la un moment dat. Dacă editați atât compose.yaml cât și haproxy.cfg în mod orb, vă face mult mai greu să spuneți dacă eșecul este o problemă de cale de fișier, o problemă de port sau o problemă de rutare.
Când aveți nevoie de dovezi rapide, păstrați aceste comenzi la îndemână:
docker compose logs haproxy
docker compose ps
docker compose up -d demo
docker compose run --rm --no-deps haproxy haproxy -V -c -f /usr/local/etc/haproxy/haproxy.cfg
sudo ss -tlnp | grep -E ':(80|8404)s' || trueAcestea sunt cele trei modele de alertă care merită recunoscute la prima vedere:
[ALERT] ... Cannot open configuration file /usr/local/etc/haproxy/haproxy.cfg : No such file or directory
[ALERT] ... Starting frontend http: cannot bind socket (Permission denied) [0.0.0.0:80]
[ALERT] ... parsing [/usr/local/etc/haproxy/haproxy.cfg:12] : unknown keyword 'chekc'; did you mean 'check' maybe?Aceasta este partea liniștitoare a unei mici implementări inițiale: formele de eșec sunt de obicei și ele mici. Nu trebuie să începeți din nou. Trebuie să identificați care strat se plânge și să corectați mai întâi acel lucru.
Unde să Mergi După Instalare
Odată ce demo-ul cu un backend funcționează, arhitectura este deja utilă. Următorul pas real este să înlocuiești containerul demo cu aplicația ta reală, păstrând aceeași structură HAProxy. După aceea, adaugă HTTPS/TLS ca pas de urmărire dedicat, și tratează rutarea pe bază de domeniu plus ACL-uri ca subiecte separate în loc să le grăbești în această primă instalare.

Când ești gata pentru mai mult de un backend, același model devine vizibil semnificativ:
backend app_backend
balance roundrobin
server app1 app1:8080 check
server app2 app2:8080 checkPentru editări sigure la o configurație bind-mounted, validează mai întâi și apoi reîncarcă HAProxy cu ușurință:
docker compose up -d demo
docker compose run --rm --no-deps haproxy haproxy -V -c -f /usr/local/etc/haproxy/haproxy.cfg
docker compose kill -s HUP haproxy📝 Notă: Pagina de statistici este intenționat doar locală în acest ghid. Dacă o expui vreodată public, adaugă mai întâi autentificare și controale de acces.
Asta te duce înapoi la problema inițială: voiai o singură ușă de intrare curată în fața unui serviciu, fără a transforma prima configurare într-un proiect complet de operații. Acum ai acea cale de lucru. Mai important, ai și modelul mental corect: HAProxy primește traficul mai întâi, îl transmite unde trebuie, și îți oferă o modalitate mai curată de a crește stiva pe un VPS auto-gestionat fără a pierde controlul configurației.
la toate serviciile de găzduire