Мережі Docker і прокидання портів
Як контейнери бачать один одного, чим публікація порту відрізняється від простого EXPOSE і чому Docker обходить правила ufw.
Оновлено 23 серпня 2026 р.
Половина проблем із контейнерами пов'язана з мережею: сервіс не бачить базу даних, порт зайнятий, панель раптом відкрита всьому інтернету. Розберімо, як це працює.
Три типи мереж
- bridge — типовий. Контейнери отримують внутрішні адреси й спілкуються між собою; назовні виходять через NAT хоста.
- host — контейнер використовує мережу хоста напряму; публікація портів не потрібна й не працює. Швидко, але без ізоляції: порт 80 контейнера — це порт 80 хоста.
- none — мережі немає взагалі. Для завдань, яким вона не потрібна.
Власна мережа, створена вами чи Compose, відрізняється від типової bridge однією важливою річчю: у ній працює розв'язання імен. Контейнер app може звернутися до контейнера db за іменем db.
docker network create backend
docker network ls
docker network inspect backend
У Compose така мережа створюється автоматично для кожного проєкту.
Публікація портів
ports:
- "127.0.0.1:8080:8080" # accessible only from the machine itself
- "80:80" # accessible to the whole internet
Ліворуч — порт на хості, праворуч — порт усередині контейнера. Директива EXPOSE в образі нічого не публікує: це лише позначка, на якому порту слухає застосунок.
Правило просте: публікуйте лише те, що має бути доступним ззовні. Базу даних, панель і внутрішній API ставте на 127.0.0.1, а вебсервер із сертифікатом відкривайте назовні.
Docker сам пише правила
iptablesі робить це до ланцюжків, якими керуєufw. Порт, опублікований як-p 8080:8080, буде доступний з інтернету, навіть якщоufwпоказує «deny». Не покладайтеся на фаєрвол — прив'яжіть порт до127.0.0.1.
Зв'язок між контейнерами
services:
app:
networks: [frontend, backend]
db:
networks: [backend]
proxy:
networks: [frontend]
ports:
- "80:80"
- "443:443"
networks:
frontend:
backend:
Тут база даних доступна лише застосунку, а назовні дивиться тільки вебсервер. Це правильна конфігурація для будь-якого стеку.
Усередині контейнера звертайтеся до сусіда за назвою сервісу: postgres://db:5432/app. Адреса localhost усередині контейнера означає сам контейнер, а не хост — це друга за поширеністю помилка після latest.
Щоб дістатися сервісу, що працює на самому хості, з контейнера, використовуйте ім'я host.docker.internal, додавши:
extra_hosts:
- "host.docker.internal:host-gateway"
Діагностика
docker compose exec app ping -c2 db
docker compose exec app getent hosts db
sudo ss -tulpn | grep LISTEN
docker port <container>
ss покаже, на якому інтерфейсі порт насправді слухає: 0.0.0.0:8080 — відкритий назовні, 127.0.0.1:8080 — ні.
Поширені помилки
- «Port already in use». Його тримає інший контейнер або служба хоста.
sudo ss -tulpn | grep :80знайде винуватця. - Контейнери з різних проєктів Compose не бачать одне одного. У кожного проєкту своя мережа. З'єднайте їх явно через
networks: { shared: { external: true } }. - Застосунок слухає
127.0.0.1усередині контейнера. Тоді публікація не допоможе: ззовні контейнера до нього не дістатися. Налаштуйте застосунок слухати0.0.0.0. - Пропускна здатність. До 500 Мбіт/с спільно на хост. Роздача файлів ділить її з усім іншим.