Quản lý dịch vụ với systemd: tạo service và timer thay cron

SSH vào VPS mới, việc đầu tiên mình thường làm là xoá bỏ cron và chuyển toàn bộ tác vụ định kỳ sang systemd timer. Lý do? Vì systemd cung cấp logging chuẩn qua journald, dependency management rõ ràng, và dễ debug khi tác vụ không chạy. Trong bài này, mình sẽ hướng dẫn bạn tạo systemd service và timer trên Ubuntu 24.04 để thay thế cron một cách triệt để.
Tóm tắt nhanh
- Systemd timer cung cấp logging qua
journalctl, kiểm tra lỗi dễ dàng hơn cron syslog. - Mỗi tác vụ gồm 2 file:
.service(định nghĩa lệnh chạy) và.timer(định nghĩa lịch). - Không cần cài thêm gói, systemd có sẵn trên mọi bản Ubuntu/Debian hiện tại.
- Có thể set dependency: chỉ chạy timer sau khi network online, hoặc sau service khác.
Yêu cầu trước khi bắt đầu
- VPS chạy Ubuntu 24.04 LTS hoặc Debian 12 (mới hơn cũng được).
- User sudo non-root (tạo user mới nếu chưa có).
- Hiểu cơ bản về systemd:
systemctl,journalctl. - Quyền root/sudo để tạo file trong
/etc/systemd/system/.
Vì sao nên dùng systemd timer thay cron?
Cron là công cụ lập lịch Unix kinh điển, nhưng nó có một số nhược điểm với sysadmin hiện đại: logging mặc định gửi về syslog, không có trạng thái rõ ràng (failed/success), và không hỗ trợ dependency. Systemd timer giải quyết những vấn đề này: bạn có thể kiểm tra trạng thái timer bằng systemctl status, xem log lỗi chi tiết bằng journalctl -u, và chỉ chạy tác vụ sau khi network, database, hoặc service khác đã sẵn sàng. Ngoài ra, timer còn hỗ trợ lịch monotonic (kiểu: chạy 30 phút sau khi boot) mà cron không làm được.
Mình từng mất nửa ngày debug một cronjob không chạy vì environment PATH khác. Với systemd, bạn có thể set Environment= rõ ràng trong file service, không còn vụ "cron không thấy lệnh" nữa. Nếu bạn đang thuê VPS Linux và muốn quản lý chuyên nghiệp, chuyển từ cron sang systemd là bước rất đáng làm.
Bước 1 - Tạo script thực thi
Mình sẽ lấy ví dụ một tác vụ backup cơ bản: nén thư mục /var/www/html và ghi vào /backup. Đầu tiên, tạo script và để ở /usr/local/bin.
sudo mkdir -p /usr/local/bin
cat << 'EOF' | sudo tee /usr/local/bin/backup-html.sh
#!/bin/bash
TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
BACKUP_DIR="/backup"
SRC_DIR="/var/www/html"
mkdir -p "$BACKUP_DIR"
tar czf "$BACKUP_DIR/backup-${TIMESTAMP}.tar.gz" -C /var/www html
# Xoá backup cũ hơn 7 ngày
find "$BACKUP_DIR" -name "backup-*.tar.gz" -mtime +7 -delete
EOF
Phân quyền thực thi cho script:
sudo chmod +x /usr/local/bin/backup-html.sh
VERIFY: Chạy thử script một lần để đảm bảo lệnh không lỗi:
sudo /usr/local/bin/backup-html.sh
ls -la /backup/
Nếu bạn thấy file .tar.gz trong /backup, script đã hoạt động.
Bước 2 - Tạo file systemd service
File service sẽ định nghĩa lệnh chạy, loại service (oneshot - chạy 1 lần rồi thoát), và các tham số môi trường. Tạo file /etc/systemd/system/backup-html.service:
sudo nano /etc/systemd/system/backup-html.service
Nội dung file:
[Unit]
Description=Backup /var/www/html to /backup
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-html.sh
User=root
Group=root
StandardOutput=journal
StandardError=journal
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
ProtectSystem=full
ReadWritePaths=/backup
[Install]
WantedBy=multi-user.target
Giải thích:
- Type=oneshot: service chạy 1 lần rồi dừng, phù hợp với script backup.
- After=network-online.target: đảm bảo network hoạt động trước khi chạy.
- ProtectSystem=full: bảo vệ các thư mục hệ thống (chỉ ghi được vào ReadWritePaths).
- Environment=PATH=...: set PATH rõ ràng, tránh lỗi thiếu lệnh.
Tải lại systemd và kiểm tra:
sudo systemctl daemon-reload
sudo systemctl start backup-html.service
sudo systemctl status backup-html.service
Output mong đợi:
● backup-html.service - Backup /var/www/html to /backup
Loaded: loaded (/etc/systemd/system/backup-html.service; static)
Active: inactive (dead) since ...
Nếu service hiện active (dead) (vì oneshot chạy xong tự thoát) và không có lỗi, bạn đã thành công.
Bước 3 - Tạo file timer
File timer sẽ định nghĩa lịch chạy. Tạo file /etc/systemd/system/backup-html.timer:
sudo nano /etc/systemd/system/backup-html.timer
Nội dung:
[Unit]
Description=Timer for backup-html service
Requires=backup-html.service
[Timer]
Unit=backup-html.service
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target
Giải thích:
- OnCalendar=daily: chạy mỗi ngày lúc 00:00 theo mặc định. Bạn có thể set chi tiết: OnCalendar=*-*-* 03:00:00 (3h sáng hàng ngày).
- Persistent=true: nếu server tắt vào thời điểm chạy, timer sẽ chạy ngay sau khi server khởi động lại.
- Requires=backup-html.service: đảm bảo service được load cùng timer.
Enable và start timer:
sudo systemctl enable backup-html.timer
sudo systemctl start backup-html.timer
sudo systemctl status backup-html.timer
Output mong đợi:
● backup-html.timer - Timer for backup-html service
Loaded: loaded (/etc/systemd/system/backup-html.timer; enabled; vendor preset: enabled)
Active: active (waiting) since ...
Dòng active (waiting) nghĩa là timer đang chờ đến thời điểm lịch.
Bước 4 - Kiểm tra danh sách timer và log
Để xem tất cả timer đang chạy:
systemctl list-timers
Output sẽ hiển thị:
NEXT LEFT LAST ...
Bạn sẽ thấy timer vừa tạo với thời gian chạy tiếp theo.
Kiểm tra log của service sau khi timer kích hoạt:
journalctl -u backup-html.service -n 20 --no-pager
Tìm dòng output từ script (nếu có echo) hoặc thông báo của systemd: Started ..., Exited with status code 0.
Bước 5 - Tuỳ chỉnh lịch OnCalendar chi tiết
Systemd hỗ trợ lịch rất linh hoạt. Dưới đây là một số mẫu hay dùng:
| Mẫu | Ý nghĩa |
|---|---|
OnCalendar=minutely | Mỗi phút (debug) |
OnCalendar=*-*-* *:00:00 | Mỗi giờ đúng |
OnCalendar=*-*-* 03:00:00 | Mỗi ngày 3h sáng |
OnCalendar=Mon..Fri 23:00:00 | T2-T6 lúc 23h |
OnCalendar=*-*-01 00:00:00 | Ngày mùng 1 mỗi tháng |
OnCalendar=*-*-1/3 00:00:00 | Mỗi 3 ngày (1,4,7...) |
Sau khi sửa timer, reload systemd và restart timer:
sudo systemctl daemon-reload
sudo systemctl restart backup-html.timer
Xử lý lỗi thường gặp
Timer không hiện trong list-timers
Chạy kiểm tra xem file timer có lỗi cú pháp không: sudo systemd-analyze verify /etc/systemd/system/backup-html.timer. Lỗi phổ biến: sai định dạng thời gian trong OnCalendar.
Service báo "Unit not found"
Nên kiểm tra tên service trong Unit= của timer phải trùng khớp tên file .service. Dùng systemctl list-units | grep backup để xem có load không. Nếu không, chạy sudo systemctl daemon-reload.
Script chạy không ra đúng output
Dùng journalctl -u backup-html.service -o cat -f để xem log realtime. Thêm exec >> /tmp/debug.log 2>&1 vào đầu script để ghi stdout/stderr ra file tạm, rất dễ debug.
Timer chạy sai giờ (múi giờ)
Mặc định systemd dùng múi giờ hệ thống. Kiểm tra bằng timedatectl. Nếu bạn cần lịch theo UTC, thêm OnCalendar=03:00:00 UTC.
So sánh nhanh: systemd timer vs cron
| Tính năng | Systemd timer | Cron |
|---|---|---|
| Logging | journald (tách riêng, filter dễ) | syslog (gộp chung, lọc phức tạp) |
| Dependency | After, Requires, Wants (có sẵn) | Không hỗ trợ |
| Lịch monotonic | OnBootSec, OnUnitActiveSec | Không hỗ trợ trực tiếp |
| Environment | Set rõ trong file service | Phải export trong script |
| Debug | systemctl status, journalctl -u | Kiểm tra mail/syslog |
| Dễ dùng với người mới | Hơi cao (2 file + nhớ lệnh) | Dễ (1 dòng trong crontab) |
Trên một VPS Linux như của thueVPS, việc dùng systemd timer thay cron không chỉ giúp bạn quản lý tác vụ chuyên nghiệp hơn mà còn dễ dàng tích hợp với các service khác. Nếu bạn mới chuyển qua, hãy bắt đầu với 1-2 tác vụ không quan trọng (ví dụ: dọn log) để làm quen, rồi dần dần chuyển toàn bộ cron cũ.
Câu hỏi thường gặp
Làm thế nào để tạo timer chạy mỗi 5 phút?
Dùng OnCalendar=*:0/5 hoặc dạng rõ hơn: OnCalendar=*-*-* *:00:00 cho mỗi giờ. Với chu kỳ sub-minute, cron vẫn là lựa chọn dễ hơn, nhưng systemd hỗ trợ thời gian đến giây, vd OnCalendar=*-*-* *:*:00 (mỗi phút).
Systemd timer có chạy khi server bị tắt đúng lúc không?
Có, nếu bạn set Persistent=true trong [Timer]. Khi server khởi động lại, timer sẽ catch up và chạy service ngay. Đây là ưu điểm lớn so với cron, vốn bỏ qua hoàn toàn nếu server tắt đúng giờ chạy.
Tôi có thể dùng timer để chạy một service khác thay vì script không?
Có. Chỉ cần set Unit=your-service.service trong file timer. Ví dụ: timer để restart Docker, hoặc timer để gọi một systemd service khác.
Timer không chạy đúng lúc, kiểm tra gì trước tiên?
Kiểm tra múi giờ hệ thống: timedatectl. Kiểm tra cú pháp OnCalendar bằng: systemd-analyze calendar "daily" (thay bằng mẫu của bạn). Nếu output báo lỗi, sửa lại ngay. Kiểm tra timer có đang active: systemctl status backup-html.timer.
Tôi muốn chạy timer ngay bây giờ để test?
Chạy sudo systemctl start backup-html.service, lệnh này kích hoạt service độc lập, không cần đợi timer. Hoặc dùng sudo systemctl start backup-html.timer và gọi sudo systemctl daemon-reload để kích hoạt timer lại.
Bài viết liên quan
- Cấu hình swap và tối ưu bộ nhớ cho VPS RAM thấp
- Tối ưu MariaDB cho VPS RAM thấp trên Ubuntu 24.04
- Gửi cảnh báo hệ thống VPS qua Telegram bot bằng script Bash
- Cấu hình logrotate quản lý log trên VPS Ubuntu


