Jak zainstalować HAProxy z Docker Compose na Ubuntu VPS
Jedna usługa sieciowa na VPS jest łatwa do bezpośredniego udostępnienia — dopóki nie chcesz jednych czystych publicznych drzwi, swobody zamiany backendu później lub bezpieczniejszego sposobu na zatrzymanie wysyłania ruchu do czegoś, co jest zepsute. To jest punkt, w którym proxy przestaje być „czymś dla dużych zespołów infrastruktury” i zaczyna być praktyczne.

HAProxy doskonale pasuje do tej roli. Pomyśl o tym jako o menedżerze ruchu siedzącym przed Twoją aplikacją: żądania trafiają najpierw do HAProxy, a HAProxy decyduje, dokąd powinny pójść dalej. Nie potrzebujesz dużego klastra, aby skorzystać z tego. Nawet na jednym VPS Ubuntu 24.04 daje ci czystszą granicę między internetem a usługą, którą faktycznie uruchamiasz.
Ten przewodnik utrzymuje pierwsze wdrożenie celowo ustrukturyzowane: jeden VPS Ubuntu 24.04, Docker Compose, jeden kontener HAProxy, jeden demo backend i dowód, że routing naprawdę działa.
Dlaczego HAProxy jest ważny, zanim go potrzebujesz
Wyobraź sobie mały VPS uruchamiający jedną aplikację, która działa dobrze dzisiaj. Odpowiada na porcie, strona się ładuje i wszystko wygląda dobrze. Problemy zaczynają się, gdy chcesz mieć stabilny publiczny punkt wejścia, możliwość zastąpienia backendu później bez zmiany publicznego adresu, lub warstwę frontową, która może przestać wysyłać ruch do usługi, która zawodzi. Bezpośrednie udostępnianie aplikacji zaczyna się wydawać niestabilne zaskakująco szybko.

Te wymagania wszystkie wskazują na tę samą brakującą warstwę: kontrolowany punkt wejścia między internetem a twoją aplikacją. HAProxy zapewnia tę warstwę. Klienci łączą się z HAProxy jako pierwsi, a HAProxy decyduje, gdzie każde żądanie idzie dalej.
Ta separacja jest przydatna nawet zanim będziesz mieć wiele serwerów. Daje ci czystszą publiczną krawędź teraz i bezpieczniejszą ścieżkę do późniejszych zmian, takich jak zastąpienie backendu, routing świadomy zdrowia i HTTPS. Reszta przewodnika pokazuje ten wzorzec w jego najprostszej działającej formie i weryfikuje go rzeczywistą ścieżką żądania.
Szybkie terminy HAProxy, które ułatwiają zrozumienie reszty tego przewodnika

Potrzebujesz tylko małego zestawu słownictwa, aby pewnie śledzić pierwsze wdrożenie HAProxy. Poniższa tabela obejmuje terminy, które mają znaczenie w tym przewodniku.
| Termin | Znaczenie w prostym języku |
|---|---|
| 🌐 reverse proxy | Usługa frontowa, która jako pierwsza odbiera żądania i przekazuje je innej usłudze wewnętrznej. |
| ⚖️ load balancer | Warstwa frontowa, która może rozprowadzać żądania na więcej niż jeden cel backend. |
| 🚪 frontend | Miejsce, w którym klienci łączą się z HAProxy. |
| 🧩 backend | Usługa lub serwer, do którego HAProxy wysyła żądanie dalej. |
| ❤️ health check | Sposób, w jaki HAProxy zauważa, czy backend powinien nadal otrzymywać ruch. |
| 🐳 image | Spakowany szablon aplikacji używany do tworzenia kontenerów. |
| 📦 container | Uruchomiona instancja obrazu. |
W tym przewodniku reverse proxy to pierwszy model mentalny, który warto mieć na uwadze. HAProxy siedzi przed czymś innym i kontroluje przekazanie. Load balancing to rozszerzona możliwość, która staje się przydatna, gdy później dodasz wiele serwerów backend.
Dwa terminy, które mają największe znaczenie po otwarciu konfiguracji, to frontend i backend. Frontend to miejsce, w którym pojawia się klient. Backend to miejsce, do którego HAProxy wysyła żądanie dalej. Health check ma znaczenie, ponieważ pozwala HAProxy zauważyć, kiedy cel powinien przestać otrzymywać ruch.
Do czego HAProxy się nadaje — i co ten przewodnik celowo pomija

Jeśli wyobrażasz sobie swój stack jako budynek biurowy, HAProxy to recepcja: ruch przychodzi tam najpierw, zostaje skierowany do właściwego pokoju i przestaje być wysyłany do pokoju, który jest wyraźnie niedostępny.
W tym przewodniku przekłada się to na trzy zadania istotne dla początkujących:
- akceptowanie przychodzących żądań HTTP
- przekazywanie ich do backendu demo
- monitorowanie, czy backend jest wystarczająco zdrowy, aby nadal otrzymywać ruch
To jest już przydatne z jednym backendem, ponieważ daje ci jedną kontrolowaną publiczną krawędź przed aplikacją.
Później ten sam wzorzec skaluje się czysto. Możesz zastąpić backend, dodać więcej backendów, wprowadzić HTTPS, lub pozwolić HAProxy rozprowadzać ruch na wiele celów zamiast tylko jednego. Aby utrzymać pierwszy przebieg jako nauczalny, ten przewodnik pozostaje w trybie HTTP i celowo pomija terminację TLS, ACL, ograniczanie szybkości, tabele stick i pary HA. To wszystko to rzeczywiste tematy HAProxy. Po prostu nie są właściwym punktem wyjścia dla pierwszego działającego wdrożenia.
Co budujesz i co musisz najpierw przygotować

Przed utworzeniem plików warto zobaczyć ostateczny kształt stosu. Wdrożenie w tym przewodniku wygląda następująco:
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 jest tutaj główną ścieżką, ponieważ utrzymuje pierwszą instalację powtarzalną, przejrzystą i łatwą do edycji. Zamiast budować niestandardowy obraz w pierwszym dniu, przechowujesz konfigurację HAProxy na hoście, montując ją w kontenerze i uruchamiając cały stos z jednego pliku. Na samodzielnie zarządzanym Ubuntu VPS — na przykład VPS AlexHost — to czysty dopasowanie, ponieważ układ pozostaje łatwy do sprawdzenia.
💡 Wskazówka: Ten przewodnik celowo używa Docker Compose plus bind-mounted haproxy.cfg. Jest to najbardziej przejrzysta ścieżka pierwszej instalacji, ponieważ możesz edytować konfigurację proxy bezpośrednio bez dodawania kroku budowania obrazu.
Przed rozpoczęciem upewnij się, że masz przygotowane te podstawy:
- Ubuntu 24.04 VPS
- Zainstalowany Docker Engine
- Docker Compose v2 dostępny przez docker compose
- Dostęp do terminala i uprawnienia do uruchamiania Docker
- Port 80 dostępny na hoście
- Ruch HTTP przychodzący dozwolony, jeśli używasz UFW lub reguł zapory po stronie dostawcy
Najpierw sprawdź wersję Ubuntu
lsb_release -a
Następnie potwierdź, że Docker i nowoczesny Compose są dostępne:
docker --version
docker compose version
Jeśli oba polecenia zwracają informacje o wersji, strona środowiska uruchomieniowego kontenera jest gotowa i możesz skupić się na HAProxy zamiast robić objazd do instalacji Docker.
Następnie upewnij się, że port 80 nie jest już w użyciu, a następnie sprawdź, czy UFW jest aktywny i czy HTTP jest już dozwolony:
sudo ss -tlnp | grep -E ':(80)s' || true
sudo ufw status
sudo ufw allow 80/tcp
✏️ UWAGA: Brak danych wyjściowych ze sprawdzenia ss zwykle oznacza, że port 80 jest wolny. Jeśli widzisz nginx, apache2, caddy lub inną usługę już słuchającą tam, napraw to najpierw. To dziesięciosekundowy krok preflight, który oszczędza wiele zamieszania później.
W powyższym przykładzie sudo ufw status pokazuje Status: active, a 80/tcp jest już obecny na liście dozwolonych. Dlatego sudo ufw allow 80/tcp zwraca Skipping adding existing rule zamiast dodawania nowej reguły. To wyjście jest normalne i po prostu oznacza, że reguła zapory była już na miejscu.
Utwórz Folder Projektu i Plik Compose
Zacznij od utworzenia małego folderu projektu dla dwóch plików, które potrzebuje to pierwsze wdrożenie:
mkdir -p ~/haproxy-docker
cd ~/haproxy-docker
Po tym układ powinien być jak najmniejszy:
~/haproxy-docker/
├── compose.yaml
└── haproxy.cfgTeraz utwórz compose.yaml i użyj tej dokładnej zawartości:
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-stoppedTen plik łączy kontenery razem, ale nie definiuje jeszcze logiki żądań HAProxy. Mówi Dockerowi, które obrazy uruchomić, które porty opublikować i skąd będzie zainstalowana konfiguracja HAProxy na hoście.
Poniższe ustawienia to te, które mają największe znaczenie dla czystego pierwszego wdrożenia:
| Ustawienie Compose | Dlaczego jest tutaj |
|---|---|
| hashicorp/http-echo:1.0 | Daje ci mały, przewidywalny backend demo bez jednoczesnego nauczania drugiego serwera WWW. |
| haproxy:3.4.1 | Używa przypiętego stabilnego tagu zamiast latest, co sprawia, że przewodnik jest mniej kruchy w czasie. |
| depends_on | Uruchamia usługę demo przed HAProxy, co jest pomocne dla kolejności pierwszego uruchomienia. |
| 80:80 | Publikuje główny listener HTTP na standardowym porcie WWW, którego oczekują czytelnicy. |
| 127.0.0.1:8404:8404 | Utrzymuje stronę statystyk dostępną do lokalnej walidacji bez domyślnego publicznego udostępniania. |
| ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro | Montuje widoczny plik konfiguracji po stronie hosta do oficjalnego obrazu HAProxy jako tylko do odczytu. |
| sysctls z net.ipv4.ip_unprivileged_port_start: “0” | Pozwala kontenerowi HAProxy bez uprawnień root wiązać się z portami niskiego poziomu, takimi jak 80. |
| restart: unless-stopped | Daje ci praktyczne domyślne ustawienie VPS: uruchom ponownie po awarii lub restarcie, ale respektuj celowe ręczne zatrzymanie. |
Jeszcze jeden szczegół ma tutaj znaczenie: w tym pliku nie ma niestandardowej sieci Docker, ponieważ Docker Compose automatycznie tworzy sieć domyślną. To daje ci DNS nazwy usług wewnątrz projektu, dlatego HAProxy będzie w stanie osiągnąć backend jako demo:5678 bez dodatkowego okablowania.
⚠️ Ostrzeżenie: Port 80 to port uprzywilejowany, więc linia sysctls nie jest dekoracyjna. Zmiana mapowania hosta na 8080:80 nie usuwa wymagania portu uprzywilejowanego wewnątrz kontenera, jeśli HAProxy nadal wiąże się z :80 wewnętrznie.
Napisz i zweryfikuj minimalny plik haproxy.cfg
Mając już wdrożoną infrastrukturę kontenerów, HAProxy nadal potrzebuje instrukcji dotyczących tego, gdzie przychodzi ruch, gdzie powinien iść i jak sprawdzana jest kondycja backendu. Utwórz haproxy.cfg:
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 /statsTo jest konfiguracja minimalna, ale nie jednorazowa. log stdout format raw local0 to przyjazny dla kontenerów wybór rejestrowania, ponieważ Docker może łatwo wyświetlić stdout, a mode http w defaults utrzymuje cały przykład w trybie HTTP, dzięki czemu zachowanie listenera i backendu pozostaje spójne i czytelne.
✏️ UWAGA: Jeden szczegół warto wyjaśnić przed omówieniem sekcji: balance roundrobin jest ustawiony jawnie, ponieważ nowsze wersje HAProxy zmieniły domyślny algorytm backendu na random, a roundrobin jest łatwiejszy do nauczenia w przewidywalny sposób na pierwszym etapie.
Oto przejrzysty opis każdej sekcji:
| Sekcja | Kluczowe linie | Co robi |
|---|---|---|
| global | log stdout format raw local0 | Wysyła logi do stdout, aby rejestrowanie Docker pozostało proste. |
| defaults | mode http, timeouty | Ustanawia bazowe zachowanie HTTP i rozsądne wartości timeoutów. |
| frontend http | bind :80, default_backend demo_backend | Tworzy publiczny listener i łączy go z definicją backendu. |
| backend demo_backend | balance roundrobin, server demo1 demo:5678 check | Mówi HAProxy, którą usługę użyć i monitorować jej kondycję. |
| frontend stats | bind :8404, stats enable, stats uri /stats | Dodaje opcjonalną lokalną stronę walidacji, aby później zobaczyć status runtime. |
Możesz zauważyć jedną brakującą rzecz: option forwardfor. To pominięcie jest celowe na tym etapie. Zachowanie oryginalnego adresu IP klienta jest przydatne później, ale to pierwsze wdrożenie dotyczy udowodnienia routingu i kondycji backendu, a nie nauczania zachowania nagłówków za pomocą kontenera demo, który nie czyni tego sygnału szczególnie wartościowym.
💡 Wskazówka: Zawsze zweryfikuj konfigurację HAProxy przed uruchomieniem pełnego stosu. Ponieważ ta konfiguracja odnosi się do backendu po nazwie usługi Compose (demo), uruchom najpierw ten backend, aby HAProxy mógł go rozwiązać podczas walidacji.
Uruchom walidację z tego samego katalogu projektu:
docker compose up -d demo
docker compose run --rm --no-deps haproxy haproxy -V -c -f /usr/local/etc/haproxy/haproxy.cfg
Jeśli drugie polecenie kończy się komunikatem Configuration file is valid, już udowodniłeś, że HAProxy może poprawnie przeanalizować plik i rozwiązać cel backendu, zanim uruchomi się jakikolwiek aktywny listener.
Uruchom Stack i Udowodnij, że Proxy Działa
Po walidacji konfiguracji uruchom stack w trybie detached:
Ponieważ krok walidacji już uruchomił demo, ta komenda głównie uruchamia HAProxy i uzgadnia pełny stack dwóch usług:
docker compose up -d
Następnie sprawdź, czy oba kontenery są uruchomione:
docker compose ps
Ten widok procesów to tylko pierwszy punkt kontrolny. Potwierdza, że Docker uruchomił kontenery, ale nie potwierdza jeszcze, że HAProxy pomyślnie kieruje ruch do backendu. Następne żądanie weryfikuje rzeczywistą ścieżkę danych.
Teraz uruchom rzeczywisty test routingu z samego VPS:
curl -i http://127.0.0.1
Sygnałem sukcesu jest HTTP/1.1 200 OK plus treść odpowiedzi zawierająca Hello from the HAProxy demo backend. Niektóre buildy http-echo zawijają ten tekst w małą odpowiedź HTML, więc skoncentruj się na frazie w treści bardziej niż na dokładnym formatowaniu.
Jeśli chcesz dowodu na poziomie przeglądarki, otwórz http://YOUR_SERVER_IP z innej maszyny.

Aby uzyskać drugą powierzchnię walidacji, sprawdź stronę statystyk tylko dla lokalnego dostępu z VPS:
curl http://127.0.0.1:8404/statsNa stronie statystyk najprzydatniejszymi sygnałami są frontend o nazwie http, backend o nazwie demo_backend, wiersz serwera o nazwie demo1, status pokazany jako UP i zwykle wartość ostatniej kontroli taka jak L4OK in 0ms. Pamiętaj też o jednej małej osobliwości Dockera: krótka składnia depends_on kontroluje kolejność uruchamiania, ale nie czeka na to, aż usługa stanie się zdrowa. Jeśli bardzo pierwsze curl raz się nie powiedzie zaraz po uruchomieniu, czekaj kilka sekund i spróbuj ponownie, zanim założysz, że konfiguracja jest błędna.
Różnicę między stanem procesu a rzeczywistym sukcesem łatwiej jest przedstawić w formie tabeli:
| Stan | Co ci to mówi |
|---|---|
| Kontenery są uruchomione | Docker uruchomił procesy. |
| curl -i http://127.0.0.1 zwraca 200 OK i frazę demo | HAProxy rzeczywiście kieruje ruch do backendu. |
| Strona statystyk pokazuje demo1 jako UP | HAProxy widzi backend jako zdrowy. |
Typowe błędy przy pierwszym uruchomieniu i szybkie rozwiązania

Jeśli konfiguracja nie działa od razu, oprzij się pokusie przepisania obu plików naraz. Większość błędów przy pierwszym uruchomieniu na tym stosie jest przewidywalna i znacznie łatwiej się je naprawia, gdy zmienia się jedną zmienną na raz.
Użyj tej matrycy jako szybkiej warstwy diagnostycznej:
HAProxy zamyka się natychmiast
Prawdopodobna przyczyna: haproxy.cfg nie istnieje.
Szybkie rozwiązanie: Upewnij się, że haproxy.cfg znajduje się obok compose.yaml.
Dlaczego się to dzieje: Oficjalny obraz nie zawiera gotowego do użytku pliku konfiguracyjnego.
Błąd mówi, że nie można otworzyć /usr/local/etc/haproxy/haproxy.cfg
Prawdopodobna przyczyna: Zła ścieżka bind-mount.
Szybkie rozwiązanie: Zweryfikuj ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro dokładnie.
Dlaczego się to dzieje: HAProxy nie może się uruchomić bez ważnego pliku konfiguracyjnego.
Błąd mówi Permission denied na porcie 80
Prawdopodobna przyczyna: Problem z wiązaniem portu uprzywilejowanego.
Szybkie rozwiązanie: Zachowaj net.ipv4.ip_unprivileged_port_start: “0” w Compose, lub przenieś zarówno HAProxy jak i opublikowany port na 8080.
Dlaczego się to dzieje: Kontener działa jako użytkownik niebędący rootem haproxy.
Zmieniłeś mapowanie na 8080:80 i nadal otrzymujesz błąd wiązania
Prawdopodobna przyczyna: HAProxy nadal wiąże się z :80 wewnątrz kontenera.
Szybkie rozwiązanie: Zmień zarówno mapowanie hosta jak i wewnętrzną linię bind, jeśli odchodzisz od portu 80.
Dlaczego się to dzieje: Reguła portu uprzywilejowanego dotyczy również wnętrza kontenera.
Port 80 jest już w użyciu
Prawdopodobna przyczyna: Inny serwis zajmuje port hosta.
Szybkie rozwiązanie: Ponownie uruchom sprawdzenie ss i zatrzymaj lub przenieś konfliktujący serwis.
Dlaczego się to dzieje: Tylko jeden proces może nasłuchiwać na tym samym porcie hosta.
Sprawdzenie składni zgłasza unknown keyword lub błędy specyficzne dla linii
Prawdopodobna przyczyna: Błąd w konfiguracji HAProxy.
Szybkie rozwiązanie: Ponownie uruchom sprawdzenie składni i napraw dokładną linię, którą zgłasza.
Dlaczego się to dzieje: Parser HAProxy jest surowy, co jest pomocne, gdy używasz go celowo.
Kontenery działają, ale curl nie zwraca odpowiedzi demo
Prawdopodobna przyczyna: Zła ścieżka routingu.
Szybkie rozwiązanie: Ponownie sprawdź default_backend demo_backend, server demo1 demo:5678 check i nazwę serwisu demo.
Dlaczego się to dzieje: Działający kontener nie jest dowodem prawidłowej ścieżki od frontendu do backendu.
Lokalny curl działa, ale witryna jest niedostępna z zewnątrz
Prawdopodobna przyczyna: Zapora sieciowa lub reguła bezpieczeństwa dostawcy.
Szybkie rozwiązanie: Otwórz port 80 w UFW i w dowolnej zaporze po stronie dostawcy.
Dlaczego się to dzieje: Publikowanie lokalne może działać nawet wtedy, gdy dostęp publiczny jest nadal zablokowany.
⚠️ Ostrzeżenie: Zmień jedną rzecz na raz. Jeśli edytujesz zarówno compose.yaml jak i haproxy.cfg na ślepo, znacznie trudniej jest stwierdzić, czy błąd to problem ścieżki pliku, problem portu czy problem routingu.
Gdy potrzebujesz szybkich dowodów, trzymaj te polecenia w pobliżu:
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' || trueTo są trzy wzorce alertów, które warto rozpoznać na pierwszy rzut oka:
[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?To jest uspokajająca część małego pierwszego wdrożenia: kształty błędów są zwykle również małe. Nie musisz zaczynać od nowa. Musisz zidentyfikować, która warstwa narzeka i najpierw poprawić tę jedną rzecz.
Dokąd Iść Po Instalacji
Gdy demo z jednym backendem działa, architektura jest już użyteczna. Następnym rzeczywistym krokiem jest zastąpienie kontenera demo Twoją rzeczywistą aplikacją, zachowując tę samą strukturę HAProxy. Następnie dodaj HTTPS/TLS jako dedykowany kolejny krok i traktuj routing oparty na domenach oraz ACL jako oddzielne tematy zamiast pośpiesznie wrzucać je do tej pierwszej instalacji.

Gdy będziesz gotów na więcej niż jeden backend, ten sam wzorzec staje się wyraźnie znaczący:
backend app_backend
balance roundrobin
server app1 app1:8080 check
server app2 app2:8080 checkAby bezpiecznie edytować konfigurację z bind-mountem, najpierw zwaliduj, a następnie gracefully przeładuj HAProxy:
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📝 Uwaga: Strona statystyk jest celowo dostępna tylko lokalnie w tym przewodniku. Jeśli kiedykolwiek ją udostępnisz publicznie, najpierw dodaj uwierzytelnianie i kontrolę dostępu.
To sprowadza Cię z powrotem do pierwotnego problemu: chciałeś jedne czyste drzwi wejściowe przed usługą, bez zamieniania pierwszej konfiguracji w pełny projekt operacyjny. Masz teraz tę działającą ścieżkę. Co ważniejsze, masz też właściwy model myślowy: HAProxy odbiera ruch jako pierwszy, przekazuje go tam, gdzie powinien, i daje Ci czystszy sposób na rozwijanie stosu na samodzielnie zarządzanym VPS bez utraty kontroli nad konfiguracją.
na wszystkich usługach hostingowych