Logrotate обычно вспоминают в двух ситуациях: когда /var/log внезапно съел весь диск или когда свежий конфиг добавили, а файл почему-то не переименовался, не сжался и не обнулился. Запросы вроде logrotate not working и logrotate debug почти всегда приводят к одному выводу: сама утилита простая, но вокруг нее есть несколько слоев — расписание, state-файл, права, владелец логов, скрипты после ротации и, отдельно, journald.
В Debian и Ubuntu ротация логов исторически запускалась из cron, а в современных установках часто работает через systemd timer. Из-за этого администратор может смотреть не туда: проверять /etc/cron.daily/logrotate, хотя реально запуск идет через logrotate.timer, или наоборот. В этой статье разберем практическую диагностику: как понять, кто запускает logrotate, как безопасно прогнать debug, почему принудительная ротация не всегда доказывает исправность, и чем ротация файловых логов отличается от ограничения журнала systemd.
Как logrotate запускается в Debian и Ubuntu
Logrotate не является постоянно работающим демоном. Он не висит в памяти и не следит за файлом в реальном времени. Это команда, которую по расписанию запускает другой механизм. Она читает конфиги, смотрит state-файл, решает, пора ли ротировать конкретный лог, выполняет действия и завершает работу.
Основные места, которые нужно проверить:
/etc/logrotate.conf— главный конфиг, обычно включает каталог/etc/logrotate.d;/etc/logrotate.d/— отдельные правила для nginx, apache, mysql, rsyslog и ваших приложений;/var/lib/logrotate/status— state-файл, где хранится дата последней ротации каждого лога;logrotate.timerиlogrotate.service— запуск через systemd;/etc/cron.daily/logrotate— запуск через cron на системах, где он еще используется.
На одном сервере не должно быть ощущения магии. Сначала выясняем, какой планировщик реально активен:
systemctl status logrotate.timer
systemctl status logrotate.service
systemctl list-timers --all | grep logrotate
ls -l /etc/cron.daily/logrotate
Если таймер включен, вы увидите ближайший и последний запуск. Если используется cron, полезно проверить пакет cron, состояние службы и системные логи cron. На минимальных образах Debian/Ubuntu cron может быть не установлен, а на некоторых серверах администратор специально переводит регулярные задачи на systemd timers.
Быстрая диагностика: что смотреть в первую очередь
Когда logrotate не работает, не начинайте с переписывания конфигов. Сначала соберите факты. В большинстве инцидентов причина находится за 5–10 минут, если идти по порядку.
- Проверьте, запускался ли logrotate вообще.
- Проверьте, нет ли ошибок синтаксиса в конфиге.
- Проверьте, считает ли logrotate, что файл уже ротировался сегодня.
- Проверьте владельца и права каталога с логами.
- Проверьте, не пишет ли приложение в старый файловый дескриптор после переименования лога.
- Проверьте, не путаете ли файловые логи с
journald.
Команды для первого осмотра:
logrotate --version
logrotate -d /etc/logrotate.conf
journalctl -u logrotate.service --no-pager
systemctl list-timers --all logrotate.timer
stat /var/lib/logrotate/status
du -h /var/log | sort -h | tail
Важный момент: режим
-dили--debugничего не ротирует. Он показывает, что logrotate сделал бы при реальном запуске. Это безопасный первый шаг на production-сервере.
Если debug говорит log does not need rotating, это еще не ошибка. Возможно, условие daily, weekly или size не выполнено. Возможно, в state-файле уже записано, что лог ротировался сегодня. А может быть, правило вообще не применяется к нужному пути.

systemd timer: как понять, что расписание работает
На современных Debian и Ubuntu logrotate часто поставляется с юнитами systemd. Таймер активирует одноразовый сервис logrotate.service. Сам сервис запускается, отрабатывает и становится inactive, что нормально. Ошибка новичка — ждать, что logrotate.service будет постоянно active.
Проверяем таймер:
systemctl status logrotate.timer
systemctl list-timers --all | grep logrotate
systemctl cat logrotate.timer
systemctl cat logrotate.service
В выводе list-timers важны поля LAST и NEXT. Если LAST пустой, таймер еще не запускался после загрузки или был отключен. Если NEXT пустой, таймер не запланирован. Если таймер disabled, включите его:
sudo systemctl enable --now logrotate.timer
systemctl list-timers --all logrotate.timer
Для ручного запуска через тот же systemd-контур используйте:
sudo systemctl start logrotate.service
journalctl -u logrotate.service --since today --no-pager
Это лучше, чем сразу запускать бинарник вручную, потому что вы проверяете именно тот способ, которым ротация будет выполняться по расписанию. Если сервис падает, журнал покажет код выхода и сообщение logrotate.
Если вы в целом переводите регулярные задачи с cron на systemd, полезно заранее разобраться с календарями, зависимостями и логированием: у нас есть отдельный практический материал про автоматизацию через systemd timers.
Иногда администратор меняет unit-файлы или делает override, но забывает перечитать конфигурацию systemd. После изменений в юнитах выполняйте:
sudo systemctl daemon-reload
sudo systemctl restart logrotate.timer
systemctl status logrotate.timer
cron: почему старый запуск может молчать
Если на сервере нет logrotate.timer или он отключен, проверьте cron. В классической схеме есть исполняемый файл /etc/cron.daily/logrotate, который вызывается ежедневным расписанием. Но cron тоже может не работать: служба остановлена, пакет удален, файл не исполняемый, сломан run-parts, или задача не попадает в окно запуска из-за выключенного сервера.
systemctl status cron
ls -l /etc/cron.daily/logrotate
run-parts --test /etc/cron.daily
grep -R logrotate /etc/cron* /var/spool/cron 2>/dev/null
На ноутбуках и нерегулярно включаемых машинах в игру может вступать anacron, но на сервере обычно ожидается круглосуточная работа. На VDS и обычных Debian/Ubuntu-серверах проще и предсказуемее использовать systemd timer: состояние видно через systemctl, логи доступны через journalctl, а расписание легко проверить одной командой.
Не держите одновременно два активных механизма, которые запускают один и тот же /etc/logrotate.conf. Обычно logrotate сам не сломается от повторного запуска, потому что ориентируется на state-файл, но вы получите путаницу в диагностике и редкие гонки на нестандартных правилах.
Debug, verbose и force: чем отличаются режимы
У logrotate есть три режима, которые часто путают.
-dили--debug— только анализ, без изменений файлов.-vили--verbose— подробный реальный запуск.-fили--force— принудительная ротация, даже если срок или размер не наступили.
Безопасная последовательность для отладки такая:
sudo logrotate -d /etc/logrotate.conf
sudo logrotate -v /etc/logrotate.conf
sudo logrotate -f -v /etc/logrotate.conf
Последнюю команду не стоит запускать бездумно на боевом сервере. --force может сжать и переименовать много логов, вызвать postrotate-скрипты, отправить сигнал сервисам и изменить state-файл. Для проверки одного правила лучше создать временный конфиг и отдельный state-файл.
sudo logrotate -d -s /tmp/logrotate-test.status /etc/logrotate.d/myapp
sudo logrotate -v -s /tmp/logrotate-test.status /etc/logrotate.d/myapp
Так вы не трогаете системный /var/lib/logrotate/status и можете спокойно повторять тесты. Это особенно удобно для новых приложений, где надо проверить create, su, postrotate и права.
State-файл: почему logrotate уверен, что уже все сделал
Файл /var/lib/logrotate/status — один из самых частых источников недоумения. Logrotate не определяет последнюю ротацию только по именам файлов .1, .gz или времени модификации. Он хранит состояние в отдельном файле. Поэтому после ручных экспериментов может казаться, что правило не работает, хотя logrotate просто видит: сегодня этот лог уже обрабатывался.
Посмотреть состояние можно так:
sudo sed -n '1,80p' /var/lib/logrotate/status
sudo grep -F '/var/log/nginx/access.log' /var/lib/logrotate/status
Удалять весь state-файл ради одной проверки — плохая привычка. Вы собьете историю для всех правил. Если нужно изолировать тест, используйте -s /tmp/logrotate-test.status. Если нужно аккуратно исправить конкретную запись, сначала сделайте копию:
sudo cp /var/lib/logrotate/status /var/lib/logrotate/status.bak
sudo grep -n '/path/to/app.log' /var/lib/logrotate/status
После ручного редактирования обязательно запустите debug. Ошибка в state-файле может остановить весь запуск. В production я предпочитаю не редактировать его без необходимости: чаще проблему решает корректный тест через отдельный state-файл или ожидание следующего нормального окна ротации.
Конфиг logrotate: минимальный рабочий пример
Допустим, приложение пишет в /var/log/myapp/app.log от пользователя www-data. Нам нужна ежедневная ротация, хранение 14 архивов, сжатие и создание нового файла с правильным владельцем.
/var/log/myapp/app.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
create 0640 www-data adm
su www-data adm
}
Что здесь важно:
dailyзадает периодичность, но не время запуска; время задает cron или systemd timer;rotate 14хранит 14 поколений;missingokне считает отсутствующий лог ошибкой;notifemptyне ротирует пустой файл;compressсжимает старые логи;delaycompressоткладывает сжатие последнего ротированного файла, что полезно для сервисов, которые могут еще писать в старый дескриптор;createсоздает новый лог после ротации;suуказывает, от чьего имени работать с логом и каталогом.
Директива su стала особенно важной после ужесточений безопасности. Если каталог логов доступен на запись не только root или имеет небезопасные права, logrotate может отказаться ротировать файл с сообщением о небезопасном родительском каталоге. В debug это обычно видно сразу.
Права и владельцы: самая частая причина отказа
Представьте каталог /var/log/myapp, которым владеет пользователь приложения. Это нормально, если приложение само пишет туда логи. Но logrotate по умолчанию стартует от root и проверяет безопасность пути. Если каталог доступен на запись группе или другим пользователям, утилита может остановиться, чтобы не выполнять опасные операции в потенциально подменяемом каталоге.
Проверяем путь:
namei -l /var/log/myapp/app.log
ls -ld /var/log /var/log/myapp
ls -l /var/log/myapp/app.log
Типичные варианты исправления:
sudo chown root:adm /var/log/myapp
sudo chmod 0755 /var/log/myapp
sudo chown www-data:adm /var/log/myapp/app.log
sudo chmod 0640 /var/log/myapp/app.log
Если каталог должен оставаться во владении пользователя приложения, добавьте в правило su user group. Например:
/var/log/myapp/*.log {
daily
rotate 7
missingok
notifempty
compress
create 0640 myapp adm
su myapp adm
}
Не лечите проблему правами 0777. Это почти всегда ухудшает ситуацию: logrotate может продолжить ругаться, а безопасность логов станет хуже. Логи часто содержат IP-адреса, user-agent, пути запросов, технические ошибки и фрагменты пользовательских данных, поэтому доступ к ним должен быть ограничен.
copytruncate или сигнал сервису: что выбрать
После переименования файла процесс может продолжать писать в старый inode. Это не баг logrotate: так работают файловые дескрипторы в Linux. Если приложение открыло app.log и держит дескриптор, простое переименование не заставит его перейти на новый файл.
Есть два подхода. Первый — попросить сервис переоткрыть логи через postrotate. Это предпочтительно для nginx, apache, rsyslog и многих демонов.
/var/log/myapp/app.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
create 0640 myapp adm
su myapp adm
postrotate
systemctl kill -s HUP myapp.service 2>/dev/null || true
endscript
}
Второй — использовать copytruncate. Тогда logrotate копирует текущий файл в архив, а исходный обрезает на месте. Приложение продолжает писать в тот же inode, и переоткрывать лог не нужно.
/var/log/myapp/app.log {
daily
rotate 7
missingok
notifempty
compress
copytruncate
su myapp adm
}
copytruncate удобен, но у него есть окно потери строк: между копированием и обрезкой приложение может что-то записать. Для высоконагруженных сервисов лучше настроить корректное переоткрытие логов или вывод в stdout с последующей обработкой системным журналом или лог-агентом. Для простых PHP- или Python-приложений с умеренным потоком логов copytruncate часто приемлем, но решение надо принимать осознанно.
journald — это не logrotate
Отдельная путаница возникает с journald. Команда journalctl показывает бинарный журнал systemd, а не обычные текстовые файлы, которые ротирует logrotate. Если сервис пишет только в stdout/stderr и запускается под systemd, его сообщения могут жить в journal, а не в /var/log/myapp.log.
Проверить размер журнала:
journalctl --disk-usage
journalctl -u nginx.service --since today --no-pager
journalctl -u myapp.service --since '1 hour ago' --no-pager
Ограничения journald задаются в /etc/systemd/journald.conf и drop-in файлах. Например:
[Journal]
SystemMaxUse=1G
SystemKeepFree=2G
MaxRetentionSec=14day
Compress=yes
После изменения настроек перезапустите журнал:
sudo systemctl restart systemd-journald
journalctl --disk-usage
Для ручной очистки есть vacuum-команды:
sudo journalctl --vacuum-time=14d
sudo journalctl --vacuum-size=1G
Logrotate не управляет размером journald. Если растет
/var/log/journal, ищите настройки systemd-journald, а не правила в/etc/logrotate.d.

Типовые ошибки logrotate not working
Ошибка синтаксиса в одном файле ломает общий запуск
Если в /etc/logrotate.d добавили правило с незакрытой фигурной скобкой или неизвестной директивой, общий запуск может завершиться ошибкой. Поэтому после любого изменения выполняйте:
sudo logrotate -d /etc/logrotate.conf
Если нужно проверить только один файл:
sudo logrotate -d /etc/logrotate.d/myapp
Путь не совпадает с реальным файлом
Банально, но встречается постоянно: приложение пишет в /var/log/myapp/app.log, а правило смотрит на /var/log/my-app/app.log. Или после деплоя путь уехал в /home/app/shared/log. Проверьте открытые файлы процесса:
sudo lsof -p $(pidof myapp) 2>/dev/null | grep log
Если pidof не подходит вашему сервису, найдите PID через systemctl status myapp.service или ps.
Файл пустой, а включен notifempty
С директивой notifempty пустой лог не ротируется. Это полезно, чтобы не плодить пустые архивы. Но при тестировании администратор иногда создает пустой файл и ждет ротации. Добавьте тестовую строку:
echo 'test log line' | sudo tee -a /var/log/myapp/app.log
Размер меньше порога
Если используется size, minsize или maxsize, внимательно проверьте логику. Например, daily вместе с minsize 100M означает: ротировать не просто каждый день, а когда наступил дневной интервал и файл достиг минимального размера. Для активных access-логов это удобно, а для редких приложений может выглядеть как поломка.
Скрипт postrotate завершается ошибкой
Секции postrotate и prerotate выполняют команды оболочки. Если там неверный путь к systemctl, сломан reload или команда возвращает ненулевой код, logrotate может завершиться ошибкой. Добавляйте устойчивые команды и проверяйте их отдельно.
sudo systemctl reload nginx
sudo systemctl kill -s HUP rsyslog.service
Для нескольких логов одного сервиса полезна директива sharedscripts, чтобы postrotate выполнился один раз, а не для каждого файла.
Как безопасно добавить ротацию для своего приложения
Практичный шаблон для сервиса myapp:
/var/log/myapp/*.log {
daily
rotate 14
missingok
notifempty
compress
delaycompress
dateext
create 0640 myapp adm
su myapp adm
sharedscripts
postrotate
systemctl kill -s HUP myapp.service 2>/dev/null || true
endscript
}
Перед применением проверьте:
sudo install -o myapp -g adm -m 0755 -d /var/log/myapp
sudo touch /var/log/myapp/app.log
sudo chown myapp:adm /var/log/myapp/app.log
sudo chmod 0640 /var/log/myapp/app.log
sudo logrotate -d -s /tmp/logrotate-myapp.status /etc/logrotate.d/myapp
Если приложение не умеет переоткрывать лог по HUP, не копируйте этот postrotate вслепую. Посмотрите документацию конкретного демона или используйте copytruncate, понимая его ограничения. Для systemd-сервисов иногда проще настроить вывод в journal и управлять хранением через journald, особенно если логи дальше забирает агент централизованного сбора.
Runbook: короткий порядок действий при инциденте
Если диск уже заполнен логами, действуйте аккуратно. Не удаляйте текущий файл лога, в который пишет процесс: место может не освободиться, пока процесс держит удаленный inode. Сначала найдите крупнейшие файлы:
sudo du -xh /var/log | sort -h | tail -30
sudo find /var/log -type f -size +500M -ls
Проверьте удаленные, но открытые файлы:
sudo lsof +L1 | grep /var/log
Если большой лог открыт процессом, лучше выполнить корректный reload сервиса или временно обрезать файл на месте:
sudo truncate -s 0 /var/log/myapp/app.log
После аварийного освобождения места настройте нормальную ротацию и проверьте ее:
sudo logrotate -d /etc/logrotate.conf
sudo systemctl start logrotate.service
journalctl -u logrotate.service --since today --no-pager
Если проблема была в journald:
journalctl --disk-usage
sudo journalctl --vacuum-size=1G
После инцидента полезно добавить мониторинг свободного места, inode и роста отдельных логов. Logrotate — это профилактика, но не система алертинга. Если приложение за час пишет десятки гигабайт, ротация лишь отсрочит отказ, а не устранит причину.
Что проверять после обновлений Debian/Ubuntu
После крупных обновлений системы или миграции с одного релиза Debian/Ubuntu на другой стоит проверить, не изменился ли способ запуска logrotate. Также обратите внимание на пакеты, которые устанавливают свои правила в /etc/logrotate.d. Конфиги могут обновиться, появятся .dpkg-old или .dpkg-dist, а локальные правки останутся в старой версии.
find /etc/logrotate.d -maxdepth 1 -type f -name '*.dpkg-*' -ls
systemctl list-timers --all | grep logrotate
sudo logrotate -d /etc/logrotate.conf
Если вы используете immutable-инфраструктуру или Ansible, храните правила logrotate в коде и прогоняйте debug в CI хотя бы синтаксически на тестовой машине. Для серверов с несколькими приложениями удобно договориться о едином формате: отдельный файл на сервис, явный su, понятный rotate, запрет на случайные 0777 и документированный способ переоткрытия логов.
Отдельно не забывайте про резервные копии конфигов и важных логов расследования. Если нужно выстроить аккуратную схему хранения, пригодится инструкция по бэкапам в S3 через restic и borg.
Итоги
В Debian и Ubuntu logrotate редко ломается сам по себе. Чаще проблема находится вокруг него: не запущен systemd timer или cron, state-файл говорит, что ротация уже была, каталог имеет небезопасные права, приложение продолжает писать в старый inode, а администратор ждет от logrotate управления журналом journald.
Мой рабочий минимум для диагностики такой: systemctl list-timers --all logrotate.timer, затем journalctl -u logrotate.service, потом logrotate -d /etc/logrotate.conf, проверка /var/lib/logrotate/status, прав через namei -l и только после этого принудительный запуск с -f, если он действительно нужен. Такой порядок экономит время и снижает риск случайно повернуть не тот лог не в тот момент.
Хорошо настроенная ротация незаметна: логи не забивают диск, архивы предсказуемо хранятся нужное число дней, сервисы корректно переоткрывают файлы, а journald живет по собственным лимитам. Именно к этому состоянию и стоит привести каждый Debian/Ubuntu-сервер, где есть веб-приложения, базы данных, прокси или фоновые воркеры.


