Як встановити HAProxy з Docker Compose на Ubuntu VPS
Один веб-сервіс на VPS легко виставити прямо в мережу — поки ви не захочете одні чисті публічні вхідні двері, свободу змінити бекенд пізніше або безпечніший спосіб припинити надсилання трафіку до чогось зламаного. Саме тоді проксі перестає здаватися “чимось для великих команд інфраструктури” і починає здаватися практичним.

HAProxy добре підходить для цієї ролі. Думайте про це як про менеджер трафіку, який сидить перед вашою програмою: запити спочатку потрапляють на HAProxy, а HAProxy вирішує, куди вони повинні йти далі. Вам не потрібен великий кластер, щоб від цього отримати користь. Навіть на одному Ubuntu 24.04 VPS це дає вам чистіший край між інтернетом і сервісом, який ви насправді запускаєте.
Цей посібник навмисне структурує першу розгортку: один Ubuntu 24.04 VPS, Docker Compose, один контейнер HAProxy, один демо-бекенд і доказ того, що маршрутизація дійсно працює.
Чому HAProxy важливий до того, як він вам знадобиться
Уявіть невеликий VPS, на якому зараз добре працює один додаток. Він відповідає на порту, сайт завантажується, і все виглядає добре. Проблеми починаються, коли вам потрібна стабільна публічна точка входу, можливість замінити backend пізніше без зміни публічної адреси, або передній шар, який може припинити відправку трафіку до служби, що відмовляє. Прямий доступ до додатка починає здаватися крихким дивовижно швидко.

Всі ці вимоги вказують на один відсутній шар: контрольовану точку входу між інтернетом і вашим додатком. HAProxy забезпечує цей шар. Клієнти спочатку підключаються до HAProxy, а HAProxy вирішує, куди далі йде кожен запит.
Це розділення корисне навіть до того, як у вас буде кілька серверів. Це дає вам чистіший публічний край зараз і безпечніший шлях до подальших змін, таких як заміна backend, маршрутизація з урахуванням здоров’я та HTTPS. Решта посібника показує цей шаблон у його найпростішій робочій формі та перевіряє його за допомогою реального шляху запиту.
Швидкі терміни HAProxy, які полегшують розуміння цього посібника

Вам потрібен лише невеликий набір словника, щоб впевнено виконати перше розгортання HAProxy. Таблиця нижче охоплює терміни, які мають значення в цьому посібнику.
| Термін | Значення простою мовою |
|---|---|
| 🌐 зворотний проксі | Фронтальний сервіс, який отримує запити першим і передає їх іншому внутрішньому сервісу. |
| ⚖️ балансувальник навантаження | Фронтальний шар, який може розподіляти запити між кількома цільовими бекендами. |
| 🚪 frontend | Місце, де клієнти підключаються до HAProxy. |
| 🧩 backend | Сервіс або сервер, до якого HAProxy далі передає запит. |
| ❤️ перевірка здоров’я | Спосіб для HAProxy помітити, чи повинен backend продовжувати отримувати трафік. |
| 🐳 образ | Упакований шаблон додатку, який використовується для створення контейнерів. |
| 📦 контейнер | Запущений екземпляр образу. |
Для цього посібника зворотний проксі — це перша концептуальна модель, яку слід мати на увазі. HAProxy розташовується перед чимось іншим і контролює передачу. Балансування навантаження — це розширена можливість, яка стає корисною, коли ви пізніше додаєте кілька серверів backend.
Два терміни, які мають найбільше значення, коли ви відкриваєте конфіг, — це frontend і backend. Frontend — це місце, де прибуває клієнт. Backend — це місце, куди HAProxy далі передає запит. Перевірка здоров’я має значення, оскільки вона дозволяє HAProxy помітити, коли ціль повинна припинити отримувати трафік.
Для чого HAProxy хороший — і що цей посібник навмисне пропускає

Якщо уявити ваш стек як офісну будівлю, HAProxy — це стійка реєстрації: трафік спочатку потрапляє туди, потім спрямовується в потрібний кабінет і перестає надсилатися в кабінет, який явно недоступний.
У цьому посібнику це перекладається на три завдання, релевантні для початківців:
- приймати вхідні HTTP запити
- перенаправляти їх на демо-бекенд
- контролювати, чи достатньо здоровий цей бекенд, щоб продовжувати отримувати трафік
Це вже корисно з одним бекендом, оскільки дає вам один контрольований публічний край перед додатком.
Пізніше той же паттерн масштабується чисто. Ви можете замінити бекенд, додати більше бекендів, запровадити HTTPS або дозволити HAProxy розподіляти трафік між кількома цілями замість однієї. Щоб зберегти перший проход навчальним, цей посібник залишається в режимі HTTP і навмисне пропускає TLS termination, ACLs, rate limiting, stick tables та HA pairs. Це все справжні теми HAProxy. Вони просто не є правильною стартовою точкою для першого робочого розгортання.
Що ви будуєте та що вам потрібно спочатку

Перед створенням файлів корисно побачити остаточну форму стека. Розгортання в цьому посібнику виглядає так:
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 — це основний шлях тут, тому що він зберігає першу установку відтворюваною, видимою та легкою для редагування. Замість створення користувацького образу в перший день ви зберігаєте конфіг HAProxy на хості, монтуєте його в контейнер і запускаєте весь стек з одного файлу. На самостійно керованому Ubuntu VPS — наприклад, на AlexHost VPS — це чистий варіант, тому що макет залишається легким для перевірки.
💡 Порада: Цей посібник навмисне використовує Docker Compose плюс bind-mounted haproxy.cfg. Це найпрозоріший шлях першої установки, тому що ви можете редагувати конфіг проксі безпосередньо без додавання кроку побудови образу.
Перед тим як почати, переконайтеся, що у вас є ці основи:
- Ubuntu 24.04 VPS
- Docker Engine встановлений
- Docker Compose v2 доступний через docker compose
- Доступ до терміналу та дозвіл на запуск Docker
- Порт 80 доступний на хості
- Вхідний HTTP дозволений, якщо ви використовуєте UFW або правила брандмауера на стороні постачальника
Спочатку перевірте версію Ubuntu
lsb_release -a
Далі підтвердьте, що Docker та сучасний Compose доступні:
docker --version
docker compose version
Якщо обидві команди повертають інформацію про версію, сторона контейнерного середовища готова, і ви можете залишитися сфокусованими на HAProxy замість того, щоб робити обхід до встановлення Docker.
Далі переконайтеся, що порт 80 вже не використовується, а потім перевірте, чи активний UFW і чи вже дозволений HTTP:
sudo ss -tlnp | grep -E ':(80)s' || true
sudo ufw status
sudo ufw allow 80/tcp
✏️ ПРИМІТКА: Відсутність виводу з перевірки ss зазвичай означає, що порт 80 вільний. Якщо ви бачите nginx, apache2, caddy або іншу службу, яка вже прослуховує там, спочатку це виправте. Це десятисекундний крок перевірки перед польотом, який економить багато плутанини пізніше.
У наведеному вище прикладі sudo ufw status показує Status: active, а 80/tcp вже присутній у списку дозволів. Тому sudo ufw allow 80/tcp повертає Skipping adding existing rule замість додавання нового. Цей вивід є нормальним і просто означає, що правило брандмауера вже було на місці.
Створіть папку проекту та файл Compose
Почніть зі створення невеликої папки проекту для двох файлів, які потрібні для цього першого розгортання:
mkdir -p ~/haproxy-docker
cd ~/haproxy-docker
Після цього макет повинен бути якомога меншим:
~/haproxy-docker/
├── compose.yaml
└── haproxy.cfgТепер створіть compose.yaml та використайте цей точний вміст:
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-stoppedЦей файл з’єднує контейнери разом, але він ще не визначає логіку запитів HAProxy. Він повідомляє Docker, які образи запустити, які порти опублікувати та звідки буде змонтована конфігурація HAProxy на хості.
Наступні параметри найважливіші для чистого першого розгортання:
| Параметр Compose | Чому він тут |
|---|---|
| hashicorp/http-echo:1.0 | Надає вам крихітний, передбачуваний демо-бекенд без одночасного навчання другого веб-сервера. |
| haproxy:3.4.1 | Використовує закріплений стабільний тег замість latest, що робить посібник менш крихким з часом. |
| depends_on | Запускає сервіс demo перед HAProxy, що корисно для порядку першого запуску. |
| 80:80 | Опублікує основний HTTP-слухач на стандартному веб-порту, який очікують читачі. |
| 127.0.0.1:8404:8404 | Зберігає сторінку статистики доступною для локальної перевірки без публічного розкриття за замовчуванням. |
| ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro | Змонтовує ваш видимий файл конфігурації на стороні хоста в офіційний образ HAProxy як тільки для читання. |
| sysctls з net.ipv4.ip_unprivileged_port_start: “0” | Дозволяє контейнеру HAProxy без прав root прив’язуватися до низьких портів, таких як 80. |
| restart: unless-stopped | Надає вам практичне стандартне значення VPS: перезапуск після помилки або перезавантаження, але дотримання навмисної ручної зупинки. |
Ще один деталь важлива тут: у цьому файлі немає користувацької мережі Docker, оскільки Docker Compose автоматично створює мережу за замовчуванням. Це дає вам DNS з назвою сервісу всередині проекту, тому HAProxy зможе досягти бекенда як demo:5678 без додаткового дротування.
⚠️ Попередження: Порт 80 — це привілейований порт, тому рядок sysctls не є декоративним. Зміна зіставлення хоста на 8080:80 не усуває вимогу привілейованого порту всередині контейнера, якщо HAProxy все ще прив’язується до :80 внутрішньо.
Напишіть та валідуйте мінімальний haproxy.cfg
З контейнерною розводкою на місці, HAProxy все ще потребує інструкцій щодо того, де надходить трафік, куди він повинен йти та як перевіряється здоров’я backend. Створіть 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 /statsЦе мінімальна конфігурація, але вона не одноразова. log stdout format raw local0 — це дружній до контейнерів вибір логування, оскільки Docker легко може вивести stdout, а mode http у defaults зберігає весь приклад у режимі HTTP, тому поведінка listener та backend залишаються послідовними та читаними.
✏️ ПРИМІТКА: Одна деталь варта уваги перед розбором розділів: balance roundrobin встановлюється явно, оскільки новіші версії HAProxy змінили алгоритм backend за замовчуванням на random, а roundrobin легше навчати передбачувано з першого разу.
Ось простий розбір кожного розділу:
| Розділ | Ключові рядки | Що це робить |
|---|---|---|
| global | log stdout format raw local0 | Відправляє логи до stdout, щоб логування Docker залишалося простим. |
| defaults | mode http, timeouts | Встановлює базову поведінку HTTP та розумні значення timeout. |
| frontend http | bind :80, default_backend demo_backend | Створює публічний listener та підключає його до визначення backend. |
| backend demo_backend | balance roundrobin, server demo1 demo:5678 check | Повідомляє HAProxy, який сервіс використовувати та моніторити його здоров’я. |
| frontend stats | bind :8404, stats enable, stats uri /stats | Додає опціональну локальну сторінку валідації, щоб ви могли бачити статус під час виконання пізніше. |
Ви можете помітити одну відсутню річ: option forwardfor. Це пропущення навмисне в базовому шляху. Збереження оригінальної IP-адреси клієнта корисне пізніше, але це перше розгортання стосується доведення маршрутизації та здоров’я backend, а не навчання поведінці заголовків з демо-контейнером, який не робить цей сигнал особливо цінним.
💡 Порада: Завжди валідуйте конфігурацію HAProxy перед запуском повного стека. Оскільки ця конфігурація посилається на backend за його ім’ям сервісу Compose (demo), спочатку запустіть цей backend, щоб HAProxy міг його розпізнати під час валідації.
Запустіть валідацію з того ж каталогу проекту:
docker compose up -d demo
docker compose run --rm --no-deps haproxy haproxy -V -c -f /usr/local/etc/haproxy/haproxy.cfg
Якщо друга команда закінчується на Configuration file is valid, ви вже довели, що HAProxy може правильно розпарсити файл та розпізнати ціль backend перед тим, як запуститься будь-який живий listener.
Запустіть Stack і доведіть, що Proxy працює
Після валідації конфігурації запустіть stack у відокремленому режимі:
Оскільки крок валідації вже запустив demo, ця команда в основному запускає HAProxy і узгоджує повний двосервісний stack:
docker compose up -d
Потім перевірте, чи обидва контейнери живі:
docker compose ps
Цей вигляд процесу — лише перша контрольна точка. Він підтверджує, що Docker запустив контейнери, але ще не те, що HAProxy успішно маршрутизує трафік на backend. Наступний запит перевіряє фактичний шлях даних.
Тепер запустіть фактичний тест маршрутизації з самого VPS:
curl -i http://127.0.0.1
Сигнал успіху — HTTP/1.1 200 OK плюс тіло відповіді, що містить Hello from the HAProxy demo backend. Деякі збірки http-echo обертають цей текст у невеликий HTML-відповідь, тому зосередьтеся на фразі тіла більше, ніж на точному форматуванні.
Якщо вам потрібен доказ на рівні браузера, відкрийте http://YOUR_SERVER_IP з іншої машини.

Для другої поверхні валідації перевірте локальну сторінку статистики з VPS:
curl http://127.0.0.1:8404/statsНа сторінці статистики найбільш корисні сигнали — це frontend з назвою http, backend з назвою demo_backend, рядок сервера з назвою demo1, статус, показаний як UP, і зазвичай значення останньої перевірки, як-от L4OK in 0ms. Також пам’ятайте одну невелику особливість Docker: коротка синтаксис depends_on контролює порядок запуску, але вона не чекає, поки сервіс стане здоровим. Якщо перший curl один раз не вдасться відразу після запуску, зачекайте кілька секунд і спробуйте ще раз, перш ніж припускати, що конфігурація неправильна.
Різницю між станом процесу та реальним успіхом легше розуміти в табличній формі:
| Стан | Що це вам говорить |
|---|---|
| Контейнери запущені | Docker запустив процеси. |
| curl -i http://127.0.0.1 повертає 200 OK і фразу demo | HAProxy насправді маршрутизує трафік на backend. |
| Сторінка статистики показує demo1 як UP | HAProxy бачить backend як здоровий. |
Поширені помилки при першому запуску та швидкі виправлення

Якщо налаштування не працює відразу, стримайте бажання переписати обидва файли одночасно. Більшість збоїв при першому запуску на цьому стеку передбачувані, і їх набагато легше виправити, коли ви змінюєте по одній змінній за раз.
Використовуйте цю матрицю як швидкий рівень діагностики:
HAProxy негайно завершує роботу
Ймовірна причина: haproxy.cfg відсутній.
Швидке виправлення: Переконайтеся, що haproxy.cfg існує поруч із compose.yaml.
Чому це відбувається: Офіційний образ не поставляється з готовою конфігурацією.
Помилка говорить, що не можна відкрити /usr/local/etc/haproxy/haproxy.cfg
Ймовірна причина: Неправильний шлях bind-mount.
Швидке виправлення: Перевірте ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro точно.
Чому це відбувається: HAProxy не може запуститися без дійсного файлу конфігурації.
Помилка говорить Permission denied на порту 80
Ймовірна причина: Проблема з прив’язкою привілейованого порту.
Швидке виправлення: Збережіть net.ipv4.ip_unprivileged_port_start: “0” у Compose, або перемістіть HAProxy і опублікований порт на 8080.
Чому це відбувається: Контейнер запускається як непривілейований користувач haproxy.
Ви змінили відображення на 8080:80 і все ще отримуєте помилку прив’язки
Ймовірна причина: HAProxy все ще прив’язується до :80 всередині контейнера.
Швидке виправлення: Змініть як відображення хосту, так і внутрішній рядок bind, якщо ви переходите від порту 80.
Чому це відбувається: Правило привілейованого порту застосовується і всередині контейнера.
Порт 80 вже використовується
Ймовірна причина: Інша служба володіє портом хосту.
Швидке виправлення: Повторно запустіть перевірку ss і зупиніть або перемістіть конфліктну службу.
Чому це відбувається: Тільки один процес може прослуховувати один і той же порт хосту.
Перевірка синтаксису повідомляє про unknown keyword або помилки, специфічні для рядка
Ймовірна причина: Помилка в конфігурації HAProxy.
Швидке виправлення: Повторно запустіть перевірку синтаксису і виправте точний рядок, який вона повідомляє.
Чому це відбувається: Парсер HAProxy суворий, що корисно, коли ви його навмисне використовуєте.
Контейнери запущені, але curl не повертає демо-відповідь
Ймовірна причина: Неправильний шлях маршрутизації.
Швидке виправлення: Повторно перевірте default_backend demo_backend, server demo1 demo:5678 check та ім’я служби demo.
Чому це відбувається: Запущений контейнер не є доказом правильного шляху від фронтенду до бекенду.
Локальний curl працює, але сайт недоступний ззовні
Ймовірна причина: Брандмауер або правило безпеки постачальника.
Швидке виправлення: Відкрийте порт 80 у UFW та будь-якому брандмауері на стороні постачальника.
Чому це відбувається: Локальна публікація може працювати навіть коли публічний доступ все ще заблокований.
⚠️ Попередження: Змінюйте по одній речі за раз. Якщо ви сліпо редагуєте як compose.yaml, так і haproxy.cfg, ви значно ускладнюєте визначення того, чи є збій проблемою шляху файлу, проблемою порту чи проблемою маршрутизації.
Коли вам потрібні швидкі докази, тримайте ці команди поруч:
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' || trueЦе три шаблони сигналів тривоги, які найбільше варто розпізнавати з першого погляду:
[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?Це заспокійлива частина невеликого першого розгортання: форми збоїв зазвичай теж невеликі. Вам не потрібно починати заново. Вам потрібно визначити, який рівень скаржиться, і спочатку виправити цю одну річ.
Куди Йти Після Встановлення
Коли односерверна демонстрація працює, архітектура вже корисна. Наступний реальний крок — замінити демо-контейнер на вашу фактичну програму, зберігаючи ту ж структуру HAProxy. Після цього додайте HTTPS/TLS як окремий наступний крок і розглядайте маршрутизацію на основі домену та ACL як окремі теми, а не поспішайте їх у це перше встановлення.

Коли ви готові до більш ніж одного бекенду, той же шаблон стає явно значущим:
backend app_backend
balance roundrobin
server app1 app1:8080 check
server app2 app2:8080 checkДля безпечного редагування конфігурації, змонтованої через bind, спочатку перевірте, а потім плавно перезавантажте 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📝 Примітка: Сторінка статистики навмисне локальна в цьому посібнику. Якщо ви коли-небудь виставляєте її публічно, спочатку додайте аутентифікацію та контроль доступу.
Це повертає вас до вихідної проблеми: ви хотіли одні чисті вхідні двері перед сервісом, не перетворюючи перше встановлення на повний операційний проект. Тепер у вас є робочий шлях. Що ще важливіше, у вас також є правильна ментальна модель: HAProxy отримує трафік першим, перенаправляє його туди, де він належить, і дає вам чистіший спосіб розвивати стек на самостійно керованому VPS без втрати контролю над конфігурацією.
на всіх хостингових послугах