OSEN-НИЙ SAAALEСкидка 50% на виртуальный хостинг и VDS
до 30.11.2025 Подробнее
Выберите продукт

cron vs systemd timers: что выбрать для задач и healthcheck

Cron до сих пор жив на большинстве серверов, но в современных Linux-дистрибутивах с systemd таймеры дают более управляемый и наблюдаемый способ запускать периодические задачи. Разберёмся, чем отличаются cron и systemd timers, когда логичнее использовать каждый из них и как перенести существующие задания с внятным healthcheck, логированием и контролем ресурсов.
cron vs systemd timers: что выбрать для задач и healthcheck

На большинстве серверов по-прежнему живёт старый добрый cron. Но во всех современных дистрибутивах Linux с systemd у нас есть альтернатива — systemd timers. Часто они уже используются самим дистрибутивом (обслуживание, обновления, ротация логов), но для своих задач админы продолжают привычно держаться за crontab.

В этой статье разберёмся, чем отличаются cron и systemd timers, когда логичнее использовать каждый из них, как правильно мигрировать периодические задачи и добавить нормальный healthcheck и логирование, вместо надежды на «наверное, оно работает».

Кратко: cron vs systemd timers

Сначала структурируем различия по сути, а не по идеологии.

Cron:

  • Простой текстовый формат расписания (5 полей + команда).
  • Минимум зависимостей, работает даже без systemd.
  • Запуск без контекста сервисов: каждая строка — отдельный шелл.
  • Логика перезапуска, лимиты ресурсов и безопасность — на совести скрипта.

Systemd timers:

  • Каждый таймер привязан к systemd-сервису.
  • Гибкий синтаксис расписания (OnCalendar, монотонные таймеры).
  • Встроенный учёт пропущенных запусков, RandomizedDelaySec, зависимостей.
  • Логи — в journald, единый механизм рестартов, лимитов ресурсов и sandboxing.

Ключевая идея: systemd timers — это не просто «ещё один cron», а расписание для systemd-сервисов, со всеми их возможностями — от рестартов и лимитов ресурсов до зависимостей и изоляции.

Когда cron по‑прежнему уместен

Несмотря на популярность systemd, полностью отказываться от cron не всегда нужно.

Типичные случаи, когда cron всё ещё ок:

  • Простые задания на shared-хостинге, где у пользователя нет доступа к systemd (типичный случай для классического виртуального хостинга).
  • Легаси-окружения без systemd (старые Debian/CentOS, контейнеры, минимальные системы).
  • Задачи, напрямую интегрированные с самим cron (например, пакеты дистрибутива, ещё не мигрировавшие на systemd timers).

Если у вас небольшой скрипт, который раз в час синхронизирует файлы или крутит «хвосты» в базе, и сервер без systemd, — нет смысла усложнять.

Схема архитектуры systemd-сервиса и связанного с ним таймера

Когда лучше перейти на systemd timers

Во всех более-менее серьёзных сценариях, особенно на VDS или выкупленных серверах с systemd, переход на таймеры оправдан.

Сигналы, что пора на systemd timers:

  • Вы хотите видеть статус задач: когда действительно запускались, что с ними, почему упали.
  • Нужно нормальное логирование в journald, интеграция с мониторингом, алёртами.
  • Задача должна перезапускаться при сбое или иметь строгие лимиты по RAM/CPU.
  • Нужна изоляция (sandbox), чёткие права, User/Group, ограничения по файловой системе.
  • Нужен детальный healthcheck: статус последнего прогона, код возврата, время выполнения.

На практике, как только задача становится хоть немного «продакшеновой» (бэкапы, интеграции с внешними API, обновление кэшей и т.п.), таймеры дают много плюсов почти бесплатно. Особенно это заметно на выделенных серверах и VDS для продакшен-проектов, где от стабильности периодических задач сильно зависит бизнес.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Базовая архитектура systemd timers

В systemd таймер — это всего лишь юнит с типом .timer, который привязан к юниту .service. Принцип:

  • Конкретную работу описываем в foo.service.
  • Расписание, когда её запускать, — в foo.timer.

Простейший пример юнита сервиса:

[Unit]
Description=Example cron-like job using systemd

[Service]
Type=oneshot
User=www-data
Group=www-data
ExecStart=/usr/local/bin/my-job.sh

И соответствующий таймер:

[Unit]
Description=Run my-job every 5 minutes

[Timer] 
Persistent=true

[Install]
WantedBy=timers.target

Далее:

systemctl daemon-reload
systemctl enable --now my-job.timer

Теперь systemd сам будет запускать my-job.service по расписанию и хранить информацию о предыдущих запусках.

Перенос простого crontab на systemd timers

Рассмотрим типичный пример из crontab -e:

*/10 * * * * /usr/local/bin/backup-db.sh > /var/log/backup-db.log 2>&1

Здесь каждые 10 минут запускается скрипт и пишет лог в файл. Чтобы перенести его в systemd, создадим два юнита: сервис и таймер.

Сервисный юнит

# /etc/systemd/system/backup-db.service
[Unit]
Description=Database backup job

[Service]
Type=oneshot
User=backup
Group=backup
ExecStart=/usr/local/bin/backup-db.sh
StandardOutput=journal
StandardError=inherit

Здесь мы убрали перенаправление в файл и используем journald. Так проще парсить логи мониторингом или собирать их в централизованное хранилище. Если вы уже строите резервные копии баз (например, по сценариям вроде описанных в руководстве по PITR для PostgreSQL), ведение логов через journald сильно упрощает отладку.

Таймер

# /etc/systemd/system/backup-db.timer
[Unit]
Description=Run database backup every 10 minutes

[Timer] 
Persistent=true
RandomizedDelaySec=60

[Install]
WantedBy=timers.target

Опции:

  • OnCalendar=*:0/10 — каждые 10 минут.
  • Persistent=true — если сервер был выключен или в спящем режиме в момент запуска, пропущенный запуск будет выполнен при старте.
  • RandomizedDelaySec=60 — небольшой рандомный джиттер, чтобы не долбить, например, внешнее хранилище строго в одном и том же моменте (актуально при нескольких серверах).

После создания юнитов:

systemctl daemon-reload
systemctl enable --now backup-db.timer

Типичные «подводные камни» cron

Почему вообще стоит думать о миграции? В бою всплывают старые проблемы cron:

  • Тихие падения: скрипт упал, о том, что задание не выполняется, узнаём через неделю от клиента.
  • Переменные окружения: в cron окружение минимальное, не подтягиваются пользовательские профили и алиасы, ломаются PATH, LANG, PYTHONPATH.
  • Параллельные запуски: если задача не успела завершиться до следующего запуска, копии накладываются, начинают конкурировать за ресурсы или данные.
  • Нет чёткого статуса: чтобы понять, когда задание выполнялось, надо вручную смотреть системные логи или почту.

Часть проблем можно лечить в самом cron (например, через flock, логирование в syslog, ручной healthcheck), но systemd timers решают многие вопросы структурно.

Как systemd timers упрощают жизнь

Статус и логи по умолчанию

Для любого таймера и сервиса есть понятный статус:

systemctl status backup-db.timer
systemctl status backup-db.service

Логи:

journalctl -u backup-db.service
journalctl -u backup-db.service --since "2025-01-01" --until "2025-01-02"

Это автоматически даёт основу для healthcheck: можно мониторить коды возврата и отсутствие запусков.

Лимиты ресурсов и безопасность

В systemd для сервиса можно задать ограничения, чего в классическом cron нет:

[Service]
User=backup
Group=backup
MemoryMax=512M
CPUQuota=50%
RuntimeMaxSec=1800
ProtectSystem=strict
PrivateTmp=true

Таким образом, периодическое задание не «унесёт» весь сервер, если что-то пошло не так.

Работа с зависимостями

Можно указать, что задача должна запускаться только после того, как поднялась база, файловое хранилище и т.д.:

[Unit]
Description=Nightly report generator
Requires=mysqld.service
After=network-online.target mysqld.service

Для cron чего-то подобного приходится городить руками через скрипты и проверки.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Healthcheck периодических задач

Одна из главных причин, по которой имеет смысл уходить от «голого» cron, — нормальный healthcheck периодических задач. В идеале мониторинг должен уметь ответить на вопросы:

  • Когда задание последний раз успешно выполнялось?
  • Сколько оно выполнялось по времени?
  • Каков код возврата?
  • Не было ли таймаута или убийства по лимитам?

Базовый healthcheck через systemd

Из коробки можно использовать:

systemctl show -p ActiveState,SubState,ExecMainStatus,ExecMainExitTimestamp backup-db.service

А также парсить:

systemd-analyze calendar "*:0/10"
systemctl list-timers --all | grep backup-db

Часто этого достаточно, чтобы на стороне мониторинга (Prometheus-экспортёр, агент, свои скрипты) периодически спрашивать статус юнита и алертить, если, например, за N минут не было успешного выполнения.

Healthcheck на уровне скрипта

Лучше всего, когда сам скрипт возвращает адекватный exit code и пишет структурированные логи. Простейший пример оболочечного скрипта с учётом healthcheck:

#!/bin/bash
set -euo pipefail

log() {
  echo "$(date -Is) [$$] $*"
}

log "Starting backup job"

if ! /usr/local/bin/do-backup; then
  log "Backup failed"
  exit 1
fi

log "Backup finished successfully"

Тогда в systemd любой ненулевой код будет отмечен как failed, что легко ловить мониторингом.

Вывод systemctl и journalctl для systemd-таймера резервного копирования

Типовые шаблоны миграции cron → systemd timers

Ежедневные ночные задания

Пример записи в crontab:

0 3 * * * /usr/local/bin/nightly-report.sh

Эквивалент в таймере:

[Timer] 
Persistent=true

Можно добавить RandomizedDelaySec=300, чтобы чуть разнести старт, если на сервере много задач в 03:00.

«Каждые N минут/часов»

Примеры cron:

*/5 * * * * /usr/local/bin/ping-api.sh
0 */2 * * * /usr/local/bin/cleanup-temp.sh

Через OnCalendar:

[Timer] 

или через монотонные таймеры:

[Timer] 
AccuracySec=30s

Монотонные таймеры удобны, когда неважно, стартует ли задача ровно на отметке, главное — интервал между завершением и следующим запуском.

Гибкость OnCalendar против формата cron

Формат OnCalendar мощнее и при этом достаточно читаемый. Примеры:

  • OnCalendar=*-*-* 03:00 — каждый день в 03:00.
  • OnCalendar=Mon..Fri 01:30 — по будням в 01:30.
  • OnCalendar=*-01-01 00:00 — каждое 1 января.
  • OnCalendar=*:0/15 — каждые 15 минут.

Понять, что именно означает выражение, можно через:

systemd-analyze calendar "Mon..Fri 01:30"

Это удобный способ проверить расписание до того, как вы примените таймер.

Параллельные запуски и блокировки

Одна из самых частых реальных проблем периодических задач — ситуации, когда новый запуск начинается до завершения предыдущего. В cron это типичная причина «просадки» сервера и гонок данных.

Варианты решения:

  • Жёсткий RuntimeMaxSec в юните сервиса — задача будет убита по таймауту.
  • Использование flock внутри скрипта, чтобы не допускать параллельных запусков.
  • Мониторинг по времени выполнения (если превышено — алерт).

Пример настройки таймаута:

[Service]
Type=oneshot
ExecStart=/usr/local/bin/long-job.sh
RuntimeMaxSec=900

И пример скрипта с flock (да, это не зависит от systemd, но хорошо комбинируется):

#!/bin/bash
set -euo pipefail

LOCKFILE=/run/long-job.lock

exec 9>"$LOCKFILE"
if ! flock -n 9; then
  echo "$(date -Is) Another instance is running, exiting"
  exit 0
fi

# Дальше безопасный код, параллельных инстансов не будет

Сравнение cron и systemd timers для healthcheck

С точки зрения наблюдаемости:

  • Cron: статус разбросан по логам, maillog, собственным логам скриптов; нужна ручная интеграция с мониторингом.
  • Systemd timers: единый статус юнита, готовые поля о последнем запуске, exit-коде, длительности; логи в journald.

Для построения healthcheck обычно достаточно поверх systemd добавить тонкий слой:

  • Скрипт или экспортёр, который смотрит systemctl show нужных юнитов.
  • Алерт, если, скажем, за последние 2 запуска хотя бы один закончился статусом failed или между успешными запусками прошло больше X минут или часов.

Где cron остаётся безальтернативным

Есть сценарии, где systemd timers недоступны или избыточны:

  • Ограниченный shared-хостинг без доступа к systemd, где можно только редактировать crontab через панель.
  • Контейнеры и минимальные образы, где systemd сознательно не используется (поднимается только один процесс).
  • Легаси-скрипты и пакеты, которые обновлять рискованно, а вы не хотите затрагивать штатный cron дистрибутива.

В таких случаях лучше «обвешать» cron теми же практиками: flock, явное логирование, нормальные exit-коды, внешние healthcheck-сервисы.

Практическая стратегия: комбинировать, а не воевать

В реальных инфраструктурах часто удобнее не устраивать религиозную войну «cron vs systemd timers», а распределить зоны ответственности:

  • Системные задачи дистрибутива — пусть остаются, как есть (часть в cron, часть уже в таймерах).
  • Все ваши продакшен-критичные задания — постепенно переводить в systemd timers с чётко описанными юнитами, логами и лимитами.
  • На shared и минимальных окружениях — аккуратно продолжать использовать cron, но по тем же принципам: явные таймауты, логика ретраев, healthcheck.

Подход «новые задачи только через таймеры» и постепенный перенос старых по мере доработок — самый безболезненный.

Выводы

Классический cron никуда не делся и вполне пригоден для простых сценариев или ограниченных окружений. Но там, где вы контролируете систему и уже используете systemd, systemd timers дают слишком много преимуществ, чтобы их игнорировать:

  • Чёткая связь задачи и сервиса.
  • Прозрачный статус и логи в journald.
  • Лимиты по ресурсам, безопасность, зависимости, sandbox.
  • Более удобный healthcheck и интеграция с мониторингом.

Если ваши бэкапы, регенерация кэшей, интеграции с внешними API, почтовые рассылки и прочие периодические задачи до сих пор живут в «темноте» cron без статуса и мониторинга — самое время переписать их на systemd timers и наконец-то увидеть, что они реально делают.

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

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

Нагрузочное тестирование staging и prod: практический гид для админов OpenAI Статья написана AI (GPT 5)

Нагрузочное тестирование staging и prod: практический гид для админов

Разберем, как системно подойти к нагрузочному тестированию веб‑проектов: чем реально отличается staging от prod, как строить профи ...
VDS: шифрование диска с LUKS2 и autounlock без ручного ввода пароля OpenAI Статья написана AI (GPT 5)

VDS: шифрование диска с LUKS2 и autounlock без ручного ввода пароля

Разберём, как включить шифрование диска с LUKS2 на VDS и не вводить пароль после каждой перезагрузки. Пошагово создадим LUKS2-том, ...
DNS-записи A, AAAA, MX, SPF, DKIM и TXT: практическое руководство OpenAI Статья написана AI (GPT 5)

DNS-записи A, AAAA, MX, SPF, DKIM и TXT: практическое руководство

DNS для админа и девопса — это конкретные записи, от которых зависят работа сайта, почта и интеграции. Разбираем на практике A и A ...