Симптом
Правите смонтированный в контейнер конфигурационный файл — nginx.conf, любой *.yml, — шлёте docker kill -s HUP для hot-reload, а изменения не применяются. Валидатор на хосте говорит OK, но контейнер ведёт себя по-старому: новых секций конфигурации внутри просто нет. Классическая ловушка bind-mount одиночного файла.
Механизм: mount держит inode, редактор его меняет
Docker bind-mount одного файла (-v /host/file.yml:/container/file.yml) привязан к inode, а не к пути. Почти все редакторы при сохранении пишут новый файл и делают rename — атомарная запись, у файла меняется inode. Старый inode, на который смотрит mount, остаётся со старым содержимым. SIGHUP заставляет процесс перечитать тот же смонтированный inode — и он честно видит старую конфигурацию.
Диагностика, подтверждающая гипотезу:
stat -c '%i' /host/path/app.yml # inode на хосте
docker exec <ctr> stat -c '%i' /etc/app/app.yml # inode в контейнере
# разные числа → это оно
# контрольный: grep -c 'новый_кусок' внутри контейнера вернёт 0
Два выхода
docker restart <ctr>— илиdocker compose up -d --force-recreate <svc>, что предпочтительнее для IaC. При старте mount резолвится по пути заново и подхватывает текущий inode. Минус — пауза сервиса около 10 секунд.- In-place truncate-write — сохранить inode и обойтись reload без рестарта: записать в тот же файл через усечение, а не через rename:
c=$(sed 's/old/new/' file); printf '%s\n' "$c" > file
# перенаправление > усекает существующий inode, не создаёт новый
ls -i file # inode до/после должен совпасть
Профилактика: монтировать директорию, а не одиночный файл (-v /host/dir:/container/dir) — тогда правки файлов внутри видны без рестарта.
Подводные камни
- Грабля повторяется, даже когда рецепт известен. Симптом каждый раз выглядит как «reload вернул 200, а не применилось» — успех HUP и вводит в заблуждение: команда сработала, но перечитала старый inode. Правило на будущее: правя любой смонтированный-как-файл конфигурационный файл, сразу планируйте restart или force-recreate, HUP не пробуйте вообще.
- Reload после правки работает только при монтировании директории.
Вывод. Bind-mount одиночного файла держит inode, а атомарная запись редактора этот inode меняет — поэтому hot-reload видит старую конфигурацию при свежей правке на хосте. Лечение: docker restart / force-recreate либо in-place truncate-write, сохраняющий inode. Профилактика: монтировать директорию, а не файл.