Новинка Виртуальный VDS сервер в Нидерландах от 490р
Выберите продукт

Debian/Ubuntu: почему logrotate не работает и как отладить ротацию через systemd

Если логи на сервере растут, а ротация молчит, проблема часто не в одном logrotate. В Debian и Ubuntu важны systemd timer, cron, права, state-файл, journald и безопасная проверка запуска.
Debian/Ubuntu: почему logrotate не работает и как отладить ротацию через systemd

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 минут, если идти по порядку.

  1. Проверьте, запускался ли logrotate вообще.
  2. Проверьте, нет ли ошибок синтаксиса в конфиге.
  3. Проверьте, считает ли logrotate, что файл уже ротировался сегодня.
  4. Проверьте владельца и права каталога с логами.
  5. Проверьте, не пишет ли приложение в старый файловый дескриптор после переименования лога.
  6. Проверьте, не путаете ли файловые логи с 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-файле уже записано, что лог ротировался сегодня. А может быть, правило вообще не применяется к нужному пути.

Схема запуска logrotate через systemd timer и service

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 и права.

FastFox VDS
Облачный VDS-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды

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 и журнала systemd journald

Типовые ошибки 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-сервер, где есть веб-приложения, базы данных, прокси или фоновые воркеры.

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

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

BorgBackup на Debian/Ubuntu: бэкап VDS по SSH через systemd timer OpenAI Статья написана AI (GPT 5)

BorgBackup на Debian/Ubuntu: бэкап VDS по SSH через systemd timer

Покажу, как на Debian и Ubuntu настроить BorgBackup для VDS: отдельный SSH-ключ, репозиторий на удалённом сервере, скрипт с borg c ...
Debian/Ubuntu: бэкапы restic, forget/prune и systemd timer OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: бэкапы restic, forget/prune и systemd timer

Разбираем рабочую схему резервного копирования Debian/Ubuntu через restic: установка, репозиторий, первый бэкап, проверка, политик ...
Debian/Ubuntu: безопасная настройка SSH-ключей на VDS OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: безопасная настройка SSH-ключей на VDS

Безопасно переводим Debian/Ubuntu на вход по SSH-ключам: создаём администратора с sudo, добавляем authorized_keys, проверяем права ...