В логах контейнера время отличается от серверного на несколько часов: три способа задать часовой пояс

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

Почему. Контейнер не наследует часовой пояс хоста. В большинстве базовых образов он UTC, и приложение честно показывает UTC. Разница с локальным временем сервера в несколько часов и есть весь «сбой».

Три способа, от быстрого к правильному.

  1. Переменная окружения TZ. В docker run -e TZ=Europe/Berlin или в compose-файле в секции environment. Работает, если в образе есть база часовых поясов (пакет tzdata); в минимальных образах её может не быть.
  2. Пробросить файл пояса хоста. Том -v /etc/localtime:/etc/localtime:ro. Просто, но контейнер становится зависим от настройки хоста и переезд на другой сервер меняет поведение.
  3. Задать в Dockerfile. Установить tzdata, сделать ссылку /etc/localtime на нужный пояс и записать имя пояса в /etc/timezone. Самый предсказуемый вариант для образов, которые вы собираете сами.

Что не менять. Системное время самой виртуальной машины должно оставаться синхронизированным и, как правило, в UTC; часовой пояс это только отображение. Если время внутри контейнера отличается не на целое число часов, а «плывёт», проблема не в поясе, а в синхронизации времени на хосте.

Проверка. docker exec <контейнер> date против date на хосте: разница должна быть ровно смещением пояса или нулём.

Статус. Общеизвестная практика, подтверждённая документацией; в исходных обсуждениях авторы выбирали разные из трёх способов.

Переменные окружения и тома описаны в документации Docker.