Systemd timer вместо cron: как перевести все бизнес-задачи без потерь — АйТи Фреш
· 11 мин чтения

Systemd timer вместо cron: как перевести бизнес-задачи без простоев

Systemd timer вместо cron: как перевести бизнес-задачи без простоев

Семёнов Евгений Сергеевич, директор АйТи Фреш. За прошлый год — два одинаковых инцидента у разных клиентов. Cron запускал pg_dump, дамп не успевал завершиться до ночи, и в 00:05 следующего дня cron поднимал второй экземпляр. Оба процесса давились на IOPS, плодили дубли файлов, бэкапили базу по половинкам. Клиенты потом разгребали консистентность несколько часов. Причина банальная — cron не знает, работает ли предыдущая задача. Лечится это переходом на systemd timer, и заодно закрывается ещё несколько хронических болячек. Статья для сисадминов на Linux: как перевести бэкапы, ротации и сервисные задачи — и зачем это вообще затевать.

Почему cron не годится для серьёзных задач

Cron пришёл из эпохи, когда сервер перезагружали раз в год, всё крутилось от root, и вопрос «а что, если задача длится дольше периода?» никого не волновал. В 2026 году его ограничения реально стоят денег:

Systemd timer — это не просто замена cron. Это таймер внутри init-системы, которая знает про сеть, про состояние сервисов, про мониторинг. Он умеет взаимодействовать с остальными компонентами — а не жить в вакууме. Если сегодня разворачиваете новый Linux-сервер, systemd timer — это дефолт, а не «попробуем».

Минимальный пример: бэкап PostgreSQL

Самый частый сценарий в нашей практике. Сервер 1С плюс PostgreSQL, каждую ночь нужен дамп базы и заброс на NAS. Пишем два unit-файла: один для сервиса, второй — для таймера.

# /etc/systemd/system/pg-backup.service
[Unit]
Description=PostgreSQL nightly backup to NAS
Requires=network-online.target
After=network-online.target postgresql.service

[Service]
Type=oneshot
User=postgres
Environment="PGPASSWORD="
ExecStartPre=/usr/bin/test -d /mnt/nas-backups
ExecStart=/usr/local/bin/pg-backup.sh
StandardOutput=journal
StandardError=journal
# Если задача упала — не пытаемся повторить в тот же день
Restart=no
# Лимит по времени (6 часов максимум)
TimeoutStartSec=6h
# /etc/systemd/system/pg-backup.timer
[Unit]
Description=Run PostgreSQL backup every day at 03:00

[Timer]
OnCalendar=*-*-* 03:00:00
# Случайное смещение до 30 минут — чтобы несколько серверов не долбились в NAS одновременно
RandomizedDelaySec=1800
# Если сервер был выключен в 03:00 — догнать после запуска
Persistent=true
Unit=pg-backup.service

[Install]
WantedBy=timers.target

Активация:

systemctl daemon-reload
systemctl enable --now pg-backup.timer
systemctl list-timers | grep pg-backup

Всё. Без единого flock-файла, без pid-файлов, без самодельных блокировок. Журнал автоматически идёт в journald, от двойного запуска защита встроенная, рандомизация — чтобы 15 серверов не долбились в NAS одновременно. Зависимости от сети — тоже.

OnCalendar: синтаксис, который нужно знать

Синтаксис сложнее, чем строка cron — не буду врать. Но гибкость несопоставима. Вот шпаргалка, которую мы раздаём администраторам клиентов при внедрении:

ЗадачаOnCalendar
Каждую минуту*:0/1:0
Каждые 15 минут*:0/15
Каждые 10 секунд*:*:0/10
Каждый час в 00 минутhourly или *-*-* *:00:00
Каждый день в 03:00*-*-* 03:00:00
По будням в 10:00Mon..Fri 10:00
Каждое воскресенье в 04:00Sun *-*-* 04:00:00
Первого числа каждого месяца*-*-01 04:00:00
Каждые 6 часов*-*-* 00/6:00:00
Каждые 15 секунд*:*:0/15

Проверка синтаксиса без запуска:

systemd-analyze calendar "Mon..Fri 10:00"
# Покажет следующие 3 запуска + оригинальный формат

Защита от двойного запуска — главная фишка

Когда задача запускается как systemd service, она получает имя unit-а. Если unit уже в состоянии active или activating — повторная попытка просто не стартует. На cron для этого ставили flock или mkdir-lock. Тут это работает из коробки, без pid-файлов и без костылей.

Реальный кейс из нашей практики: торговая компания, 38 рабочих мест. Каждые 15 минут синхронизация прайс-листов с 1С на сайт. Python-скрипт, который иногда работает 5 минут, иногда 20. На cron при проблемах с сетью скрипты накапливались — 5-6 процессов ломились в одну таблицу PostgreSQL и создавали deadlock. Перевели на systemd timer. Одна задача за раз. Пропущенные запуски видны в журнале. Нагрузка на 1С упала на 15%.

Persistent: что делать с выключенным сервером

Опция Persistent=true в timer означает: если запланированный запуск был пропущен (сервер выключен, перезагрузка), он выполнится сразу при следующем старте таймера. Для бэкапов и отчётов это спасение.

Конкретный случай: в январе 2026 клиент-медклиника перезагрузила сервер 3 января в 14:00 для обновления. Очередной бэкап 1С был запланирован на 03:00 3 января — в cron-версии он был бы просто потерян. В systemd timer с Persistent=true после перезагрузки timer увидел, что 03:00 уже прошло, стартовал задачу в 14:03. Клиент узнал о «неудачном» окне только по логам, но бэкап состоялся.

Реальные use-case из моей практики

Ротация сертификатов Let's Encrypt

# /etc/systemd/system/acme-renew.timer
[Timer]
OnCalendar=*-*-* 04:30:00
RandomizedDelaySec=3600
Persistent=true

# service вызывает certbot renew с reload nginx через deploy-hook

Очистка журналов WAL PostgreSQL

# /etc/systemd/system/pg-wal-cleanup.timer
[Timer]
OnCalendar=hourly
Unit=pg-wal-cleanup.service

# service запускает pg_archivecleanup с удержанием последних 48h

Мониторинг SSL-сертификатов на всех доменах клиента

# Каждые 6 часов проверка expired
OnCalendar=*-*-* 00/6:00:00
# Если осталось меньше 14 дней — алерт в Telegram
# service: /usr/local/bin/ssl-check --alert-days 14

Ротация логов в приложении

# Каждую ночь + persistent
OnCalendar=daily
Persistent=true
# service: compress + аплоад на S3 + удаление >30d

Обновление GeoIP и Spamhaus zone-list

# Каждые 12 часов с разбросом 2ч
OnCalendar=*-*-* 02,14:00:00
RandomizedDelaySec=7200

Миграция с cron — пошагово

Вот последовательность, которую мы применяем у клиентов при переводе с cron:

  1. Выгружаем текущий crontab всех пользователей: for u in $(cut -f1 -d: /etc/passwd); do echo "=== $u ==="; crontab -u $u -l 2>/dev/null; done.
  2. Также смотрим /etc/cron.{hourly,daily,weekly,monthly} и /etc/cron.d/.
  3. Делим задачи на три группы: критичные (бэкапы, ротация, cleanup), сервисные (GeoIP-обновления, sync-скрипты) и временные — одноразовые, legacy, от прошлых администраторов.
  4. Для критичных пишем пару unit+timer в /etc/systemd/system/. Используем готовые шаблоны.
  5. Сервисные — аналогично, но с меньшим приоритетом и менее жёсткими зависимостями.
  6. Временные и legacy — оставляем в cron как есть. Нет смысла тратить время на задачи, которые скоро уйдут.
  7. После запуска timer — удаляем соответствующую строку из crontab и ставим комментарий # migrated to systemd timer /etc/systemd/system/pg-backup.timer.
  8. Через 2 недели проверяем systemctl list-timers --all и логи — убеждаемся, что всё работает.

Чего не получится сделать

Честно про то, где systemd timer не выигрывает — чтобы не было разочарований:

Кейс: миграция 140 cron-джобов у хостинг-клиента

В феврале 2026 года к нам обратилась компания с 14 серверами в двух дата-центрах — Москва и Новосибирск, Debian 12 и Astra Linux 1.7. 1С-кластер, веб-серверы, почтовик, CRM. Суммарно 140 cron-джобов: часть от предыдущего администратора, часть от приходящего подрядчика. Периодически что-то падало. Ночная нагрузка на файловый сервер превышала норму. Бэкапы иногда тихо пропускались — и это обнаруживалось уже при попытке восстановления.

Что мы сделали за 8 рабочих дней:

  1. Собрали все crontab и /etc/cron.* в один репозиторий для аудита. Выяснилось: из 140 джобов 26 были дубли, 11 — мёртвые ссылки, 18 — устаревшие.
  2. Написали Ansible-плейбук, который раскладывает шаблоны unit и timer на все 14 серверов за один прогон.
  3. Мигрировали критические задачи: 32 backup-джоба, 18 cleanup, 14 ротаций сертификатов, 9 sync-скриптов.
  4. Добавили RandomizedDelaySec от 5 до 30 минут на все ночные backup-задачи — нагрузка на NAS в окне 02:00-04:00 упала на 40%, бэкапы стали завершаться стабильно.
  5. Настроили Zabbix: systemctl list-timers --all --no-pager --output=json парсится каждые 15 минут, алерт на любой unit в состоянии failed.
  6. За первый месяц после перехода — ноль дублирующихся запусков, ноль пропущенных бэкапов. Три уведомления о падении скриптов, которые раньше в cron просто проглатывались незаметно.

Стоимость проекта — 145 000 руб. Три backup-задачи, которые раньше падали раз в неделю, теперь стабильны. Однажды клиент уже окупил это вложение: бэкап, который при старой схеме был бы пропущен, оказался нужен — и был на месте.

Переведём ваши cron-задачи на systemd timer — от 18 000 руб.

Я лично планирую и провожу миграцию cron→systemd timer на Linux-серверах в Москве и области. Аудит существующих cron-джобов, разделение на группы, Ansible-шаблоны, интеграция с Zabbix/Prometheus. Типовой проект для 1-5 серверов — 2-3 дня. Для распределённой инфраструктуры — 1-2 недели. Первый аудит бесплатно.

Телефон: +7 903 729-62-41
Telegram: @ITfresh_Boss
Семёнов Евгений Сергеевич, директор АйТи Фреш

FAQ — systemd timer

В чём главное преимущество systemd timer перед cron?
Четыре отличия: 1) Защита от двойного запуска — если предыдущая задача ещё работает, новая не стартует. 2) Persistent — пропущенные запуски выполняются после перезагрузки. 3) Логи автоматически идут в journald без ручного перенаправления. 4) Зависимости — задачу можно привязать к готовности сети, монтированию диска, другим сервисам.
Нужно ли переписывать все cron-джобы сразу?
Нет. У меня на серверах часто мирно живут оба: systemd timer для критичного (бэкапы, ротация ключей, cleanup), cron для однострочников и старых скриптов. Переход делается поэтапно, начиная с самых болезненных задач — у которых были случаи простоя или дублирования.
Как проверить статус всех таймеров на сервере?
Команда systemctl list-timers показывает все активные таймеры, время следующего запуска, время последнего и unit-файл. Для детального просмотра одного — systemctl status backup.timer. Логи выполнения — journalctl -u backup.service.
Что такое RandomizedDelaySec и зачем оно для бизнеса?
Случайное смещение внутри окна. Если 20 серверов одновременно в 3:00 ночи делают бэкап на один и тот же файловый сервер, он ложится. RandomizedDelaySec=1800 добавляет к каждому 0-30 минут случайно — нагрузка размазывается. В продовых кластерах это обязательная настройка.
Systemd timer работает на Astra Linux и РЕД ОС?
Да, и там, и там systemd используется как init-система, timer-модуль полностью совместим. На Astra Linux 1.7/1.8 и РЕД ОС 8 наши шаблоны timer/service работают без изменений. Это даёт преимущество перед cron при миграции на российский Linux — единый подход на всём парке.

Подпишитесь на рассылку ITfresh

Каждую неделю мы выпускаем практические гайды для руководителей IT и системных администраторов. Это не просто теория! Здесь вы найдёте всё: безопасность, 1С, миграции, резервные копии и проверенные лайфхаки из наших реальных проектов.

Реквизиты оператора персональных данных

ООО «АЙТИ-ФРЕШ», ИНН 7719418495, КПП 771901001. Юридический адрес: 105523, г. Москва, Щёлковское шоссе, д. 92, корп. 7. Контакт: info@itfresh.ru, +7 903 729-62-41. Оператор обрабатывает e-mail подписчика в целях рассылки информационных и рекламных материалов до момента отзыва согласия.