Введение / Зачем это нужно
Cron — проверенный временем планировщик, но systemd таймеры предлагают современную альтернативу с лучшей интеграцией в систему, централизованным логированием через journalctl и более гибким синтаксисом расписания. Миграция позволит вам контролировать задачи через systemctl, настраивать зависимости от других сервисов и избегать проблем с окружением. После выполнения этого гайда ваши автоматические задачи будут управляться единообразно со всеми системными сервисами.
Требования / Подготовка
- Система с systemd: virtually все современные дистрибутивы Linux (Ubuntu 20.04+, Debian 11+, RHEL 8+).
- Права администратора (root или sudo) для создания файлов в
/etc/systemd/system/и управления сервисами. - Доступ к существующим cron-задачам: знание их расписания и выполняемых команд.
- Базовое понимание 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 # Сервис считается активным после завершения
Проверка результата
- Логирование: все выводы
stdout/stderrсервиса попадают в журнал systemd. Просмотрите их:journalctl -u backup.service -b # Логи последней загрузки journalctl -u backup.service --since "1 hour ago" - Тестовый запуск: принудительно запустите сервис, чтобы убедиться в работоспособности команды:
sudo systemctl start backup.service sudo systemctl status backup.service - Расписание: убедитесь, что таймер корректно вычисляет следующее время запуска (
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 таймеры — это инвестицию в стабильность и управляемость вашей системы. Вы получаете единый инструмент для всех периодических задач, детальное логирование и возможность построения сложных зависимостей. Начните с одной не критичной задачи, протестируйте её, и постепенно переносите остальные.