ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

systemd-resolved и Docker: что происходит с /etc/resolv.conf и почему не работает DNS в контейнерах

Типичная поломка на Ubuntu/Debian: на хосте DNS работает, а в Docker-контейнерах появляется Temporary failure in name resolution. Разберём, почему в /etc/resolv.conf оказывается 127.0.0.53, как Docker формирует resolv.conf, и какие правки реально чинят DNS.
systemd-resolved и Docker: что происходит с /etc/resolv.conf и почему не работает DNS в контейнерах

Симптомы: «containers cannot resolve» и странный DNS 127.0.0.53

Классическая ситуация на Ubuntu/Debian: на хосте интернет есть, apt или curl работают, а внутри контейнера DNS «умирает». В логах и выводах обычно встречается что-то из этого набора:

Temporary failure in name resolution
Could not resolve host
lookup registry-1.docker.io: no such host

Если заглянуть внутрь контейнера, часто видно, что /etc/resolv.conf указывает на nameserver 127.0.0.53. На хосте это нормально: так работает stub-резолвер systemd-resolved. Но в контейнере такой адрес почти всегда означает проблему.

Причина простая: 127.0.0.53 — loopback-адрес. Внутри контейнера loopback — это «сам контейнер», а не хост. DNS-сервера на 127.0.0.53 в контейнере нет, поэтому запросам некуда уходить.

Как Docker формирует DNS в контейнере

Docker не «угадывает» DNS, он формирует контейнерный /etc/resolv.conf из доступных источников конфигурации. Чаще всего используются:

  • настройки, видимые через /etc/resolv.conf на хосте (часто это symlink);
  • параметр --dns при запуске контейнера;
  • настройка dns на уровне Docker daemon (через /etc/docker/daemon.json);
  • параметр dns в Docker Compose для конкретного сервиса.

Когда на хосте активен systemd-resolved, файл /etc/resolv.conf нередко указывает на stub 127.0.0.53. Docker переносит это внутрь контейнера, и начинается «containers cannot resolve».

Почему на хосте это работает, а в контейнере нет

systemd-resolved слушает 127.0.0.53 в сетевом пространстве имён хоста. Контейнер — другое сетевое пространство, и его 127.0.0.1/127.0.0.53 не имеет отношения к хостовому stub-резолверу.

Отдельно полезно помнить: поведение может отличаться между машинами. Например, где-то /etc/resolv.conf уже содержит «реальные» адреса DNS (роутер, провайдер, корпоративный резолвер) и контейнеры живут нормально. А на сервере с systemd-resolved вы видите только 127.0.0.53 — и всё ломается.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Если вы держите несколько проектов и сетей на одном сервере, проще диагностировать и чинить такие вещи, когда есть полный контроль над системой и сетевым стеком на VDS: можно аккуратно настроить DNS для Docker и не зависеть от «магии» окружения.

Проверка /etc/resolv.conf и symlink на Ubuntu/Debian перед настройкой DNS для Docker

Быстрая диагностика: что именно у вас сейчас

Сначала проверьте хост: куда указывает /etc/resolv.conf, запущен ли systemd-resolved и какие upstream DNS реально используются.

ls -l /etc/resolv.conf
cat /etc/resolv.conf
systemctl status systemd-resolved --no-pager
resolvectl status
ss -lntup | grep ':53'

Типичные варианты, которые вы увидите:

  • /etc/resolv.conf содержит nameserver 127.0.0.53 и комментарии про systemd-resolved;
  • /etc/resolv.conf является symlink на файл в /run/systemd/resolve/;
  • в resolvectl status видны Upstream DNS Servers (это как раз адреса, которые обычно и нужно дать контейнерам).

Далее проверьте, что видит контейнер:

docker run --rm busybox cat /etc/resolv.conf
docker run --rm busybox nslookup example.com

Если внутри видите 127.0.0.53 — вы практически гарантированно попали в конфликт systemd-resolved и Docker.

Надёжное решение №1: задать DNS для Docker daemon (daemon.json)

Самый предсказуемый вариант для серверов — задать DNS на уровне демона Docker. Тогда Docker перестанет «перетаскивать» stub-адрес и будет прописывать в контейнеры то, что вы укажете.

Создайте или отредактируйте файл:

sudo mkdir -p /etc/docker
sudo nano /etc/docker/daemon.json

Пример конфигурации (подставьте ваши DNS: провайдерские/корпоративные или публичные):

{
  "dns": ["1.1.1.1", "8.8.8.8"],
  "dns-search": []
}

Перезапустите Docker:

sudo systemctl restart docker

Проверьте, что новые контейнеры получают правильный /etc/resolv.conf:

docker run --rm busybox cat /etc/resolv.conf
docker run --rm busybox nslookup example.com

Это влияет на DNS всех контейнеров на хосте. В продакшене обычно это плюс: меньше сюрпризов при перезагрузках и обновлениях сети.

Решение №2: переключить /etc/resolv.conf хоста на upstream DNS от systemd-resolved

У systemd-resolved есть два «варианта» resolv.conf:

  • stub-файл со 127.0.0.53 (удобно для приложений на хосте);
  • файл со списком upstream DNS (реальные адреса), обычно /run/systemd/resolve/resolv.conf.

Если сделать так, чтобы /etc/resolv.conf указывал на upstream-версию, Docker начнёт копировать нормальные DNS в контейнеры.

Сначала посмотрите, что лежит в каталоге resolve:

ls -l /run/systemd/resolve
cat /run/systemd/resolve/resolv.conf
cat /run/systemd/resolve/stub-resolv.conf

Дальше аккуратно переключите symlink:

sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf

И перезапустите Docker:

sudo systemctl restart docker

Нюанс: на системах с VPN и «динамической» сетью некоторым хостовым приложениям удобнее работать именно через stub. На типичных серверных конфигурациях это обычно не мешает, но после изменения обязательно перепроверьте резолвинг на хосте и в контейнерах.

Решение №3: указать DNS точечно (Compose или docker run)

Если вы не хотите менять глобальное поведение Docker, задайте DNS для конкретного контейнера/сервиса.

docker run

docker run --rm --dns 1.1.1.1 --dns 8.8.8.8 busybox nslookup example.com

docker-compose.yml

services:
  app:
    image: your-image
    dns:
      - 1.1.1.1
      - 8.8.8.8

Плюс — маленький радиус изменений. Минус — легко забыть добавить настройку в новый сервис, а ещё DNS может отличаться между окружениями без явной причины.

Что делать, если вам нужен split-DNS от systemd-resolved

Частый кейс: на хосте есть VPN, и systemd-resolved распределяет DNS по доменным зонам (например, публичные домены резолвятся через один набор серверов, а *.corp — через другой). Контейнеры при этом должны резолвить и внешние, и внутренние имена.

В таком сценарии простое "dns": ["8.8.8.8"] может сломать внутренние зоны. Рабочие подходы обычно такие:

  • использовать корпоративные DNS напрямую в /etc/docker/daemon.json (если они доступны по сети контейнеров);
  • поднять локальный рекурсивный резолвер на хосте (например, Unbound) и указать контейнерам адрес, доступный из docker bridge (не loopback);
  • проверить маршрутизацию: контейнеры должны уметь ходить к DNS, которые выдаёт VPN, по сети (а не через 127.0.0.53).

Правило простое: контейнеру нужен DNS-адрес, который достижим из его сети. Loopback хоста для этого почти никогда не подходит.

Проверка после исправления: чек-лист

1) На хосте

resolvectl status
cat /etc/resolv.conf

2) В новом контейнере

docker run --rm busybox cat /etc/resolv.conf
docker run --rm busybox nslookup example.com
docker run --rm busybox nslookup registry-1.docker.io

3) Проверка реального сценария (pull/build)

Если проблема проявлялась при сборке образа или установке пакетов, прогоните тест:

docker pull alpine:latest
docker build --no-cache -t dns-test .

Важно проверять именно на новых контейнерах: контейнерный /etc/resolv.conf генерируется Docker’ом при старте.

Частые ошибки и анти-паттерны

Подсовывать контейнеру 127.0.0.53 «как есть»

Почти всегда неверно. Даже если «завелось» в одной сети, при изменении маршрутов, VPN или правил фильтрации ломается одним из первых.

Править /etc/resolv.conf внутри контейнера руками

Изменения не переживут пересоздание контейнера. Делайте настройку через Docker daemon, Compose/CLI или через устойчивую схему DNS.

Забывать про особенности окружения (VDS/сервер, несколько проектов, разные сети)

Если вы держите несколько стеков на одном хосте, разумнее привести DNS к предсказуемому виду на уровне демона Docker и инфраструктуры. Для такого сценария удобнее, когда вы полностью контролируете сеть и системные службы на VDS.

Проверка DNS в новом контейнере: resolv.conf, nslookup и тест pull/build после исправления

Если контейнеры обслуживают сайт или админку, не забудьте про транспорт: корректный DNS — база, но для продакшена также важны SSL-сертификаты, чтобы исключить ошибки доверия и проблемы с безопасностью при обновлениях и интеграциях.

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Итоги

Проблема systemd-resolved и Docker — это не «мистика», а несовпадение сетевых пространств: 127.0.0.53 работает на хосте, но не является DNS-сервером внутри контейнера.

Самые практичные исправления: явно задать DNS в /etc/docker/daemon.json или переключить /etc/resolv.conf хоста на upstream-файл /run/systemd/resolve/resolv.conf. После правок всегда проверяйте резолвинг на новых контейнерах и в вашем реальном сценарии (pull/build/apt/pip).

Поделиться статьей

Вам будет интересно

Nginx/PHP-FPM: Too many levels of symbolic links (ELOOP) — как найти и исправить symlink loop при деплое OpenAI Статья написана AI (GPT 5)

Nginx/PHP-FPM: Too many levels of symbolic links (ELOOP) — как найти и исправить symlink loop при деплое

ELOOP («Too many levels of symbolic links») часто всплывает после деплоя через симлинк current в связке Nginx и PHP-FPM. Разберём ...
Nginx 499 Client Closed Request: причины, тайминги и как снизить процент обрывов OpenAI Статья написана AI (GPT 5)

Nginx 499 Client Closed Request: причины, тайминги и как снизить процент обрывов

Код 499 в Nginx означает, что клиент закрыл соединение до ответа сервера. Это может быть браузер, мобильная сеть, gRPC‑клиент или ...
Nginx: large_client_header_buffers и ошибка 400 Request Header Or Cookie Too Large OpenAI Статья написана AI (GPT 5)

Nginx: large_client_header_buffers и ошибка 400 Request Header Or Cookie Too Large

Ошибка 400 Request Header Or Cookie Too Large в Nginx обычно связана с раздутыми cookie или длинными заголовками из цепочки прокси ...