Linux

Миграция с cron на systemd таймеры: полное руководство по замене

Это руководство поможет вам перевести существующие cron-задачи на современные systemd таймеры. Вы получите более гибкое управление, логирование и интеграцию с системой, сохранив всю функциональность.

Обновлено 8 апреля 2026 г.
15-30 мин
Средняя
FixPedia Team
Применимо к:systemd 235+Ubuntu 20.04+Debian 11+RHEL/CentOS 8+Fedora 32+

Введение / Зачем это нужно

Cron — проверенный временем планировщик, но systemd таймеры предлагают современную альтернативу с лучшей интеграцией в систему, централизованным логированием через journalctl и более гибким синтаксисом расписания. Миграция позволит вам контролировать задачи через systemctl, настраивать зависимости от других сервисов и избегать проблем с окружением. После выполнения этого гайда ваши автоматические задачи будут управляться единообразно со всеми системными сервисами.

Требования / Подготовка

  1. Система с systemd: virtually все современные дистрибутивы Linux (Ubuntu 20.04+, Debian 11+, RHEL 8+).
  2. Права администратора (root или sudo) для создания файлов в /etc/systemd/system/ и управления сервисами.
  3. Доступ к существующим cron-задачам: знание их расписания и выполняемых команд.
  4. Базовое понимание systemd: рекомендуется предварительно ознакомиться с основами systemd сервисов.

Шаг 1: Анализ существующих cron-задач

Прежде чем создавать новые юнит-файлы, полностью документируйте каждую cron-задачу.

# Просмотр cron-задач текущего пользователя
crontab -l

# Просмотр системных cron-задач (требует sudo)
sudo cat /etc/crontab
sudo ls -la /etc/cron.d/ /etc/cron.hourly/ /etc/cron.daily/

Для каждой задачи запишите:

  • Расписание (например, 0 2 * * * — каждый день в 2:00).
  • Выполняемую команду и её аргументы.
  • Пользователя, от которого она запускается (для системных crontab).
  • Необходимые переменные окружения (если есть).

Пример мигрируемой задачи из /etc/crontab:

0 3 * * * root /usr/local/bin/backup.sh --rotate --compress

Шаг 2: Создание юнит-файла сервиса (.service)

Сервисный файл описывает, что выполнять. Создайте файл /etc/systemd/system/backup.service.

[Unit]
Description=Ежедневное резервное копирование данных
Documentation=man:backup.sh(8)
# Зависит от сети, если скрипт работает по сети
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
# Команда из cron
ExecStart=/usr/local/bin/backup.sh --rotate --compress
# Если нужно, укажите пользователя/группу (по умолчанию root)
# User=backupuser
# Group=backupgroup
# Импорт переменных окружения, если они были в crontab
# EnvironmentFile=/etc/default/backup
# Поведение при ошибках (по умолчанию restart=on-failure для не-oneshot)
# Restart=no

[Install]
WantedBy=multi-user.target

⚠️ Важно: Для разовых задач (Type=oneshot) systemd считает сервис завершённым после выполнения ExecStart. Если нужно выполнить несколько команд, используйте скрипт или несколько ExecStart.

Шаг 3: Создание юнит-файла таймера (.timer)

Таймерный файл описывает, когда запускать сервис. Создайте файл /etc/systemd/system/backup.timer.

[Unit]
Description=Таймер для ежедневного резервного копирования
# Убедитесь, что сервис готов к запуску (опционально)
Requires=backup.service

[Timer]
# Календарное расписание: каждый день в 3:00
OnCalendar=*-*-* 03:00:00
# Альтернатива: интервал от последнего успешного запуска
# OnUnitActiveSec=1d
# Что делать, если задача пропущена (например, при выключении ПК)
# Persistent=true
# Задержка случайная (anti-thundering herd), если нужно
# RandomizedDelaySec=10min

[Install]
WantedBy=timers.target

Синтаксис OnCalendar:

  • *-*-* 03:00:00 — ежедневно в 03:00.
  • Mon *-*-* 02:30:00 — каждый понедельник в 02:30.
  • *-*-1 00:00:00 — в первый день каждого месяца.
  • 00/30:00:00 — каждые 30 минут (в 00 и 30 минут каждого часа).

Шаг 4: Активация и проверка таймера

После создания файлов загрузите конфигурацию в systemd и активируйте таймер.

# Перечитать конфигурации systemd (обязательно после создания новых юнит-файлов)
sudo systemctl daemon-reload

# Включить и сразу запустить таймер
sudo systemctl enable --now backup.timer

# Проверить статус таймера
systemctl status backup.timer

# Посмотреть список всех активных таймеров
systemctl list-timers --all

Пример вывода list-timers:

NEXT                        LEFT          LAST                        PASSED       UNIT                ACTIVATES
Sun 2026-04-13 03:00:00 UTC 4h left       Sat 2026-04-12 03:00:00 UTC 14h ago      backup.timer        backup.service

Шаг 5: Миграция сложных сценариев

Задачи с переменными окружения

Если в cron использовались переменные (PATH, MAILTO и т.д.), перенесите их в секцию [Service]:

[Service]
Environment="PATH=/usr/local/sbin:/usr/local/bin"
Environment="BACKUP_DIR=/var/backups"
# Или импортируйте из файла
EnvironmentFile=/etc/sysconfig/backup

Задачи, запускаемые от не-root пользователя

Создайте юнит-файлы в домашней директории пользователя (~/.config/systemd/user/) и используйте systemctl --user:

mkdir -p ~/.config/systemd/user/
# Копируем .service и .timer файлы в эту директорию
systemctl --user daemon-reload
systemctl --user enable --now mytask.timer
# Включите lingering для автозапуска без входа в систему
sudo loginctl enable-linger $USER

Задачи с несколькими командами

Вынесите команды в отдельный скрипт (как в примере выше) или используйте несколько ExecStart:

[Service]
Type=oneshot
ExecStart=/usr/bin/step1.sh
ExecStart=/usr/bin/step2.sh
RemainAfterExit=yes  # Сервис считается активным после завершения

Проверка результата

  1. Логирование: все выводы stdout/stderr сервиса попадают в журнал systemd. Просмотрите их:
    journalctl -u backup.service -b  # Логи последней загрузки
    journalctl -u backup.service --since "1 hour ago"
    
  2. Тестовый запуск: принудительно запустите сервис, чтобы убедиться в работоспособности команды:
    sudo systemctl start backup.service
    sudo systemctl status backup.service
    
  3. Расписание: убедитесь, что таймер корректно вычисляет следующее время запуска (systemctl list-timers).

Возможные проблемы

Таймер не активируется после перезагрузки Убедитесь, что таймер включен (systemctl is-enabled backup.timer должен вернуть enabled). Если disabled, выполните systemctl enable backup.timer.

Сервис завершается с ошибкой, но таймер продолжает работать По умолчанию systemd считает таймер активным, даже если сервис упал. Чтобы таймер отключался при сбое, добавьте в [Timer]: FailureAction=stop. Также проверьте логи сервиса (journalctl -u backup.service).

Расписание срабатывает не в то время Проверьте часовой пояс системы (timedatectl) и синтаксис OnCalendar. Учтите, что OnUnitActiveSec считает интервал от последнего успешного запуска, а не от запланированного.

Не работают переменные окружения из crontab Cron автоматически устанавливает минимальный PATH и другие переменные. В systemd их нужно задавать явно через Environment или EnvironmentFile.

Дополнительные настройки

Отложенный запуск (RandomizedDelaySec)

Для распределения нагрузки добавьте в [Timer]:

RandomizedDelaySec=15min
OnCalendar=*-*-* 03:00:00

Это запустит задачу в случайный момент в интервале 03:00–03:15.

Запуск только при наличии сети

Если задача требует интернета, добавьте в [Unit] сервиса:

After=network-online.target
Wants=network-online.target

Это гарантирует запуск только после полного поднятия сети.

Ограничение времени выполнения

Чтобы задача не висела вечно, задайте таймаут в [Service]:

TimeoutStartSec=1h  # Прервать, если не завершится за час
TimeoutStopSec=5min

Заключение

Миграция с cron на systemd таймеры — это инвестицию в стабильность и управляемость вашей системы. Вы получаете единый инструмент для всех периодических задач, детальное логирование и возможность построения сложных зависимостей. Начните с одной не критичной задачи, протестируйте её, и постепенно переносите остальные.

Часто задаваемые вопросы

Что делать, если systemd таймер не запускается по расписанию?
Можно ли использовать переменные окружения из crontab в systemd?
Как перенести задачу, которая запускается каждые 5 минут?
Нужно ли перезагружать систему после создания юнит-файлов?

Полезное

Анализ существующих cron-задач
Создание юнит-файла сервиса (.service)
Создание юнит-файла таймера (.timer)
Активация и запуск таймера
Перенос сложных cron-задач

Эта статья помогла вам решить проблему?