Skip to content
VDS · Docker і контейнери

Мережі 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 Мбіт/с спільно на хост. Роздача файлів ділить її з усім іншим.