Latest / Linux Server Admin with Fexingo: Sysadmin, Bash, and Server Engineering / How to Use Linux Systemd Timers for Scheduled Tasks
Transcript
- Lucas: So you've been using cron for years to schedule your nightly backups, your log rotations, your certificate renewal scripts. It works — until it doesn't. And when it fails, good luck finding out why. Luna: Right. Cron's logging is basically non-existent unless you wrap every job in a custom logger. And you can't really inspect a cron job's state at runtime. Lucas: Exactly. That's where systemd timers come in. They're not new — systemd has had them since version 183 — but most sysadmins I talk to still haven't made the switch. And honestly, once you get used to them, you don't go back. Luna: Okay, so what makes a systemd timer better than a cron job? Let's start with the basics. Lucas: A systemd timer is just a unit file that triggers a service unit. You create a.timer file and a corresponding.service file. The timer defines when to run, and the service defines what to run. That separation alone is huge — you can run the service manually for testing, and you get full systemd logging and journald integration. Luna: So you can check the status of a timer just like any other systemd unit? Like 'systemctl status mytimer.timer'? Lucas: Exactly. And you can see the next trigger time, the last trigger time, whether it missed any runs, and the full journal of the service. Try getting that from cron. So let's walk through a concrete example. Say we want to rotate logs every day at 2 AM. Here's the timer unit, we'll call it logrotate.timer. Luna: Alright. What's in the file? Lucas: You start with. Then. The key directive is OnCalendar, which takes a calendar event expression. For daily at 2 AM, you write 'daily' or more precisely '-* 02:00:00'. Then you set Persistent=true so that if the server was off at 2 AM, it runs the missed job immediately on boot. Then with WantedBy=timers.target. Luna: And the service unit? Just a simple one-shot service that calls logrotate? Lucas: Right. The service unit, logrotate.service, needs with Type=oneshot and ExecStart=/usr/sbin/logrotate /etc/logrotate.conf. That's it. Then you enable and start the timer with systemctl enable --now logrotate.timer. Cron is replaced. Luna: Nice. But the calendar syntax can get complex. What about 'every weekday at 3:30 PM' or 'the first Monday of every month'? Can systemd handle that? Lucas: It can, but it takes some getting used to. Systemd uses its own calendar event format, which is more powerful than cron's — but also more verbose. For example, 'every weekday at 3:30 PM' would be 'Mon..Fri 15:30:00'. For the first Monday of every month, you'd write 'Mon -1..7 02:00:00' — meaning Monday, any month, days 1 through 7, at 2 AM. Luna: Okay, that's a bit more typing. But you get to test it with 'systemd-analyze calendar' before deploying, right? Lucas: Yes! That's a huge advantage. You run 'systemd-analyze calendar "Mon -1..7 02:00:00"' and it tells you the next few trigger times, the previous one, and whether the expression is valid. No more guessing if your cron syntax is right. Luna: So debugging is built in. What about timezones? Cron uses the system timezone, but what if my server is in UTC and I want the timer to fire in Eastern time? Lucas: Systemd timers support timezone offsets directly in the calendar expression. You just add ':America/New_York' or any IANA timezone. For example, '-* 02:00:00 America/New_York'. And you can set the timer's Timezone= directive in the section. That way your backup runs at 2 AM Eastern regardless of where the server is. Luna: That is way cleaner than having to write a wrapper script that adjusts for timezone. So far, I'm liking this. But there must be some downsides. What about dependencies between jobs? Lucas: Actually, systemd handles dependencies better than cron. You can use After=, Requires=, or even make one timer depend on another unit. For example, you could have a database backup timer that only runs after the database service is active. In cron, you'd have to script that check yourself. Luna: And what about random delays, so that not all servers hit the disk at exactly the same time? Like cron's random sleep? Lucas: Systemd timers have a RandomizeDelaySec= directive. You set it to, say, 30 minutes, and systemd will delay the start by a random amount between zero and that value. That's perfect for staggering jobs across a fleet of servers. Luna: Alright, I'm sold. But let's talk migration. If I have a hundred cron jobs, I'm not going to rewrite them all overnight. What's a practical approach? Lucas: Start with the most critical ones — the ones where failure is hardest to detect. For each one, create a timer and service pair. Keep the crontab entry in place until you've tested the timer. Then disable the cron job. You don't have to do it all at once. Luna: And what about monitoring? If a cron job fails, you might get an email. What does systemd give you? Lucas: Systemd itself doesn't email you, but you can configure journald to forward logs, or you can use something like systemd tty ask password agent or hook into a monitoring tool. But the key is that the failure is logged persistently with structured metadata. You can write a simple script that checks 'systemctl is-failed logrotate.service' and alert if it's failed. That's more reliable than parsing mail. Luna: True. And you can use OnFailure= in the service unit to trigger a notification service. That's a nice pattern. Lucas: Right. So overall, systemd timers give you integration with the init system, better logging, dependency management, timezone support, and easy debugging. The main cost is learning the calendar syntax and writing two files instead of one crontab line. Luna: But for anyone managing more than a handful of servers, that cost is quickly repaid. So if you're still on cron, maybe give systemd timers a try this week. Lucas: And speaking of tools that make your life easier, one thing we love about this show is that we don't have to interrupt the content with ad reads. That's intentional — we think your time is better spent on the technical deep dive. Luna: Yeah, the ad-free approach is something we hear a lot of listeners appreciate. If you find value in keeping the show independent and commercial-free, you can support it directly. Lucas: The link is buy me a coffee dot com slash fexingo. No pressure, just an option if you want to help us keep it this way. Luna: And that support goes right into production time — researching episodes like this one, testing configurations, and making sure we deliver something you can actually use. Lucas: Alright, back to timers. One more thing I want to mention: you can also use monotonic timers, like 'OnBootSec=5min' or 'OnUnitActiveSec=1h'. These are great for recurring tasks that don't need a fixed wall-clock time, like checking a health endpoint every hour. Luna: Right, because monotonic timers are relative to boot or the last trigger. So if your server restarts, the timer starts counting from boot. That's simpler than calculating the next cron run after a reboot. Lucas: Exactly. You can even combine them — have a timer that runs on boot and then every hour after that. Just set both OnBootSec and OnUnitActiveSec. But be careful: if the service takes longer than the interval, you can get overlapping runs. Use AccuracySec to control the granularity. Luna: So what's the best resource to learn the full calendar syntax? The man page is dense. Lucas: I'd recommend 'man systemd.time' for the calendar format, and then 'systemd-analyze calendar' to test expressions. There's also a cheat sheet on the freedesktop.org wiki. But honestly, the best way is to start with a simple timer and build up. Luna: Alright, so to summarize: systemd timers are a solid replacement for cron, offering better logging, dependency handling, timezone support, and debugging. Start with one job, test it, and migrate at your own pace. Lucas: And if you have a cron job that's been driving you crazy because it fails silently, that's the first one to convert. You'll notice the difference the next time something goes wrong. Luna: Thanks, Lucas. Next time we'll talk about using systemd journald for centralized logging across multiple servers. That should be fun. Lucas: Looking forward to it. Until then, keep your servers happy.