Фоновые задачи на проекте — это бэкапы, рассылки, очистка кэша, регенерация картинок, сбор метрик, обработчики очередей (queue workers) и многое другое. На виртуальном хостинге этим чаще управляют через cron и crontab, а на VDS — через связку systemd‑unit + systemd‑timer. Важно не просто «что-то запустить раз в час», а обеспечить надёжный график, логи и email alerts, чтобы поломки не оставались незамеченными. Про удалённые резервные копии см. отдельный разбор: S3‑бэкапы с restic/borg.
Cron на виртуальном хостинге: crontab, окружение и логи
На виртуальном хостинге cron обычно доступен пользователю через crontab -e. В нём задаём расписание, переменные окружения и команды. Минимальный набор — явно определить SHELL и PATH, а также MAILTO для уведомлений.
# /var/spool/cron/username (редактируется через crontab -e)
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@site.tld
# Каждые 5 минут: запуск планировщика фреймворка
*/5 * * * * /usr/bin/php /home/username/site/artisan schedule:run >> /home/username/logs/schedule.log 2>&1
# Ночной бэкап с пониженным приоритетом
22 3 * * * ionice -c2 -n7 nice -n 10 /home/username/bin/backup.sh >> /home/username/logs/backup.log 2>&1
Ключевые моменты:
- Используйте абсолютные пути к бинарям и скриптам (никаких относительных
./script.sh). - Задачи, которые пишут в stdout/stderr, отправятся на почту, если задан
MAILTO. Если почта недоступна, перенаправляйте вывод в файл:>> file.log 2>&1. - Если окружение в cron отличается от интерактивной сессии, добавьте
PATHи нужныеexportв начале crontab или внутри скрипта.
Анти-дубли: защита от параллельных запусков
Обычная проблема cron — наложение запусков, когда предыдущая задача не успела завершиться. Простой способ — flock с неблокирующим режимом:
* * * * * flock -n /tmp/wp-cron.lock -c '/usr/bin/php /home/username/public_html/wp-cron.php' >> /home/username/logs/wp-cron.log 2>&1
Это гарантирует, что одновременно будет работать только один экземпляр.
Логирование на хостинге: файлы и ротация
Если у вас нет доступа к logrotate, создайте каталог ~/logs и пишите туда. Ротацию можно сделать отдельным cron‑заданием, оставляя, скажем, 14 дней логов:
15 4 * * * find /home/username/logs -type f -name '*.log' -mtime +14 -delete
Для разбора проблем используйте согласованный формат сообщений: добавляйте метки времени внутри скриптов, логируйте начало/конец, количество обработанных элементов и коды возврата.
Email alerts на cron
Варианты уведомлений:
MAILTO— базовый способ: любой вывод скрипта уйдёт письмом.- Избирательная отправка при ошибке:
cmd || echo "cmd failed on $(hostname)" | mail -s 'Cron error' admin@site.tld. - Явные коды возврата: убедитесь, что ваши скрипты завершаются с
exit 1при ошибке, а не молчат.
Если локальная почта недоступна, логируйте в файл и читайте хвост логов при отладке. Продвинутый вариант — отправка в мониторинг из скрипта (CLI‑клиенты или API).
systemd timers на VDS: точность, контроль и наблюдаемость
На VDS systemd‑таймеры чаще всего комфортнее cron: выше точность расписания, встроенная устойчивость к рестартам (Persistent=true), логирование через journald, OnFailure‑хуки, перезапуски сервисов, ограничения ресурсов и sandboxing. Если под фоновые задачи нужен отдельный сервер — посмотрите гайд по выбору: как подобрать VDS по CPU/RAM.
Базовый шаблон: .service + .timer
Создаём сервис единичного запуска:
# /etc/systemd/system/site-backup.service
[Unit]
Description=Nightly site backup
[Service]
Type=oneshot
User=www-data
Group=www-data
WorkingDirectory=/var/www/example
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
ExecStart=/usr/local/bin/backup.sh
SyslogIdentifier=site-backup
StandardOutput=append:/var/log/site-backup.log
StandardError=append:/var/log/site-backup.log
Таймер с ежедневным расписанием и защитой от пропусков:
# /etc/systemd/system/site-backup.timer
[Unit]
Description=Run site backup at 03:30 daily
[Timer] 03:30:00
Persistent=true
RandomizedDelaySec=5m
AccuracySec=1m
Unit=site-backup.service
[Install]
WantedBy=timers.target
Активация:
systemctl daemon-reload
systemctl enable --now site-backup.timer
systemctl list-timers --all
Логи с journald и в файлы
По умолчанию вывод сервиса идёт в journald. Для разбора:
journalctl -u site-backup.service --since today
journalctl -u site-backup.service -f
Если нужно — параллельно пишем в файл: StandardOutput=append:/var/log/site-backup.log, StandardError=append:/var/log/site-backup.log. Ротацию файлов оформите в /etc/logrotate.d/site-backup.
Алерты отказов: OnFailure и перезапуски
Хук OnFailure позволяет дернуть другой unit при неуспехе:
# В site-backup.service
[Unit]
Шаблон уведомителя:
# /etc/systemd/system/notify-admin@.service
[Unit]
Description=Notify admin about failure of %i
[Service]
Type=oneshot
User=root
ExecStart=/usr/bin/mail -s "Unit %i failed on %H" admin@site.tld
Для длительно работающих задач добавьте устойчивость и лимиты перезапусков:
[Unit]
StartLimitBurst=5
StartLimitIntervalSec=300
[Service]
Restart=on-failure
RestartSec=5s
Расписания OnCalendar и тонкая настройка
OnCalendar=Mon..Fri 09:00— по будням в 9:00.OnCalendar=*:0/15— каждые 15 минут.OnCalendar=monthly— начало месяца.RandomizedDelaySec=— размажьте старт, чтобы не ловить «пиковый час».Persistent=true— если сервер был выключен в момент запуска, задача стартует при ближайшей возможности.
User timers: без root и изолированно
Можно запускать таймеры от конкретного пользователя, не трогая систему:
systemctl --user enable --now my-task.timer
systemctl --user list-timers
journalctl --user -u my-task.service -f
Unit‑файлы кладём в ~/.config/systemd/user/. Удобно для проектных окружений и CI‑агентов.

Длинные процессы и queue workers: nohup, cron и systemd
Обработчики очередей (queue workers) должны работать постоянно и перезапускаться при сбоях. На VDS это проще всего делать через systemd‑сервис с Restart=always и ограничениями ресурсов. На виртуальном хостинге иногда помогает nohup как временная мера.
VDS: systemd‑сервис для очередей
# /etc/systemd/system/app-queue.service
[Unit]
Description=Application queue worker
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/app
Environment="APP_ENV=prod"
ExecStart=/usr/bin/php artisan queue:work --sleep=3 --tries=3 --memory=256
Restart=always
RestartSec=5
# Лимиты и приоритеты
Nice=5
IOSchedulingClass=best-effort
IOSchedulingPriority=7
CPUQuota=50%
MemoryMax=512M
StandardOutput=append:/var/log/app-queue.log
StandardError=append:/var/log/app-queue.log
[Install]
WantedBy=multi-user.target
Далее: systemctl enable --now app-queue, затем journalctl -u app-queue -f. Такой подход надёжнее чем nohup, поскольку systemd контролирует процесс, перезапускает и пишет логи.
Виртуальный хостинг: временный запуск через nohup
Если нет systemd, можно стартовать воркер фоном и «отвязать» от текущего TTY:
nohup /usr/bin/php /home/username/site/artisan queue:work --sleep=3 --tries=3 >> /home/username/logs/queue.log 2>&1 & disown
Чтобы воркер не копился в нескольких экземплярах, используйте flock или pid‑lock внутри скрипта. Также добавьте «сторожок» в crontab, который проверяет жив ли процесс и поднимает его при падении.
wp-cron: перевод на реальный cron или systemd timers
Стандартный wp-cron запускается посетителями сайта и может «спать» на низкой посещаемости. Лучше отключить псевдо‑cron и запускать планировщик по расписанию.
Отключение wp-cron в WordPress
// wp-config.php
define('DISABLE_WP_CRON', true);
Запуск из crontab
*/5 * * * * /usr/bin/php /home/username/public_html/wp-cron.php --doing-cron >> /home/username/logs/wp-cron.log 2>&1
Запуск через systemd timer
# /etc/systemd/system/wp-cron.service
[Unit]
Description=Run WordPress cron
[Service]
Type=oneshot
User=www-data
WorkingDirectory=/var/www/site
ExecStart=/usr/bin/php wp-cron.php --doing-cron
StandardOutput=append:/var/log/wp-cron.log
StandardError=append:/var/log/wp-cron.log
# /etc/systemd/system/wp-cron.timer
[Unit]
Description=Every 5 minutes WordPress cron
[Timer]
Persistent=true
Unit=wp-cron.service
[Install]
WantedBy=timers.target
Такой запуск стабилен при низкой посещаемости и даёт прозрачные логи.

Отладка, безопасность и «краевые случаи»
Надёжное расписание — это не только «когда запускать», но и «как ограничить вред от ошибок».
- Окружение. Задавайте
PATH,LANG, переменные приложения. Для сложных проектов храните их в .env и загружайте в начале скрипта. - Таймзона. Cron и systemd timers используют системную зону. Решите заранее — локальное время или UTC. При переходах на летнее/зимнее время используйте
OnCalendarи проверяйте смещение. - Ресурсы. На VDS ограничивайте потребление:
CPUQuota,MemoryMax,IO*. На виртуальном хостинге снижайте приоритет черезnice/ionice. - Файловые права. Скрипты выполняются от пользователя cron или systemd‑пользователя. Не пишите логи туда, где нет прав; проверьте владельца каталога логов.
- Коды возврата. Делайте явный
exit 1при ошибке и логируйте причину, иначе мониторинг не поймёт, что случилось. - Тишина — враг. Даже при успехе полезно печатать короткую сводку: «processed=123 duration=4.2s» — это упростит и автоматический разбор, и ручную отладку.
- Готовность зависимостей. Для сервисов, которым нужен БД/сеть, укажите
After=network-online.targetили ретраи в скрипте. В cron — добавьте повтор с задержкой.
Чек‑лист перед запуском в прод
- Команда стабильно отрабатывает вручную и в «чистом» окружении.
- Есть защита от параллельных запусков:
flockили внутренние замки. - Логи пишутся и ротируются; при ошибке есть email alerts.
- Для долгоживущих процессов — systemd с
Restart=и ресурсными лимитами. - Расписание покрывает перерывы:
Persistent=trueдля timers. - Проверены права доступа и таймзона.
Частые ошибки и как их избежать
- Относительные пути и «оно у меня в шелле работает». Лечится явными путями и
PATHв unit/crontab. - Случайные дубли задач. Используйте замки или «единственный исполнитель».
- Молчаливые падения. Лечится кодами возврата, логами и email alerts.
- Неуправляемые воркеры. На VDS — только systemd, а не вечный
nohup. - Гигантские лог‑файлы. Включите ротацию и срок хранения.
Итог
Для хостинга — crontab с грамотным окружением, flock, логи и почтовые уведомления. Для VDS — связка systemd timers + сервисные юниты: чёткие расписания, перезапуски, OnFailure, journald и лимиты ресурсов. Отдельно держите под контролем очереди: воркеры должны быть перезапускаемы и наблюдаемы. Такой фундамент снимает массу проблем и делает фоновые задачи предсказуемыми.


