Are you still using cron jobs to keep RHEL systems up to date
Keeping a Red Hat Enterprise Linux (RHEL) server updated is the single most effective way to secure it against vulnerabilities. While manual updates give you control, they are easy to forget.
This guide covers the standard method for standalone servers (dnf-automatic), why you should avoid old-school cron jobs, and how to handle mission-critical fleets.
Why dnf-automatic is the new (recommended) way to apply automatic updates
In the past, admins often wrote simple cron jobs like 0 4 * * * dnf update -y. It works even today.
But for RHEL 9, dnf-automatic is the tool for this job. dnf-automatic is a systemd timer that can update RHEL systems at scheduled intervals. It runs in the background without interfering other running processes.
dnf-automatic gives you several benefits over chron for automating RHEL updates:
- Locking Safety:
dnf-automaticintelligently handles "yum locks." If you are manually installing a package when the cron job kicks off, a raw cron command will likely fail or corrupt the database.dnf-automaticwaits or handles this gracefully. - Granular Control: It allows you to separate the download phase from the install phase (e.g., download during the day, install at night).
- Better scheduling: Systemd timers support RandomizedDelaySec to avoid thundering herd, easy time changes with systemctl edit, and Persistent timers that catch up after downtime. Cron may miss runs unless anacron is in place and configured.
- Reporting: It has built-in "emitters" to send emails or log specific outcomes, whereas cron just dumps output to a local mailbox that is rarely checked.
- Security Filters: It natively supports filtering updates to install only security patches, leaving feature updates for manual review.
- Reboot when required:
dnf-automaticcan reboot your systems if any update requires a reboot to apply.chrondoes not have a straightforward way to achieve the same.
How to use dnf-automatic
The dnf-automatic package is not installed by default on RHEL. Follow these steps to set it up for daily automatic updates.
Step 1: Install the package
sudo dnf install dnf-automatic -y
Step 2: Configure the behavior
You can configure dnf-automatic via /etc/dnf/automatic.conf.
Open this file in your text editor and update:
# /etc/dnf/automatic.conf
[commands]
upgrade_type = default # use "security" for security-only updates
apply_updates = yes
download_updates = yes
random_sleep = 360 # adds up to 6 minutes of jitter
reboot = when-needed
reboot_command = "shutdown -r +5 'Rebooting after applying package updates'"
[emitters]
system_name = your-hostname
emit_via = motd # or "email" if you configure email settings
[base]
debuglevel = 1
These are the key parameters that you need to set:
- apply_updates: Change this to
yes(default isno, which only downloads). - upgrade_type: Change to
securityif you want to minimize the risk of breaking apps, or leave asdefaultto update everything. - reboot: Certain updates like Linux kernel updates, require a reboot to take effect. Set the value of this parameter to
when-neededsodnf-automaticwill trigger a reboot whenever an updated needs a reboot to apply. - emitters: Set to
emit_via = motdto see update status when you log in, or configure email settings lower in the file.
Step 3: Enable dnf-automatic timer
RHEL 9 uses systemd timers to run dnf-automatic.
Enable dnf-automatic.timer.
$ sudo systemctl enable --now dnf-automatic.timer
Note the use of .timer.
If you are familiar with systemd services (like chronyd) you use commands like systemctl enable chronyd.service to enable such services as systemd Service units.
In the case of dnf-automatic, we are enabling a Timer unit.
Important Distinction: Timer vs. Service:
In the specific case of your dnf-automatic command, you must be careful which unit you enable.
Do NOT enable: dnf-automatic-install.service (If you enable the service, it attempts to run the update logic on every single boot, which slows down startup, and it won't repeat daily.)
DO enable: dnf-automatic-install.timer (This starts the clock that triggers the service once per day.)
Step 3: Verify dnf-automatic is running
Check the status of dnf-automatic timer unit:
$ systemctl status dnf-automatic.timer
● dnf-automatic.timer - dnf-automatic timer
Loaded: loaded (/usr/lib/systemd/system/dnf-automatic.timer; enabled; preset: disabled)
Active: active (waiting) since Fri 2026-01-16 12:01:18 +0530; 1s ago
Until: Fri 2026-01-16 12:01:18 +0530; 1s ago
Trigger: Sat 2026-01-17 06:39:51 +0530; 18h left
Triggers: ● dnf-automatic.service
We can see it's in active state.
Step 4: How to verify dnf-automatic update runs
Check the time of the next run:
$ systemctl list-timers dnf-automatic.timer
NEXT LEFT LAST PASSED UNIT ACTIVATES
Sat 2026-01-17 06:10:02 +0530 15h left Fri 2026-01-16 12:01:18 +0530 2h 20min ago dnf-automatic.timer dnf-automatic.service
Look for the value of NEXT to see the scheduled time for the next run of dnf-automatic. The default log fine for dnf-automatic is dnf.rpm.log. You can check it for a history of packages installed and updated by dnf-automatic.
# View the last 20 lines of the DNF log
$ tail -n 20 /var/log/dnf.rpm.log
Here's a sample output of /var/log/dnf.rpm.log where dnf-automatic is scheduled to run at 6.00 AM everyday.
2026-01-23T06:08:56+0530 SUBDEBUG Upgrade: podman-6:5.6.0-12.el9_7.x86_64
2026-01-23T06:08:56+0530 SUBDEBUG Upgrade: glib2-2.68.4-18.el9_7.1.x86_64
2026-01-23T06:08:56+0530 SUBDEBUG Upgraded: podman-6:5.6.0-11.el9_7.x86_64
2026-01-23T06:08:56+0530 SUBDEBUG Upgraded: glib2-2.68.4-18.el9_7.x86_64
Step 5: (Optional) Change the run time
You can change the run time and several other parameters of systemd timers.
$ sudo systemctl edit dnf-automatic.timer
This opens dnf-automatic.timer config file in your default text editor.
### Editing /etc/systemd/system/dnf-automatic.timer.d/override.conf
### Anything between here and the comment below will become the new contents of the file
[Timer]
OnCalendar=*-*-* 6:00
### Lines below this comment will be discarded
### /usr/lib/systemd/system/dnf-automatic.timer
# [Unit]
# Description=dnf-automatic timer
# # See comment in dnf-makecache.service
# ConditionPathExists=!/run/ostree-booted
# Wants=network-online.target
#
# [Timer]
# OnCalendar=*-*-* 6:00
# RandomizedDelaySec=60m
# Persistent=true
#
# [Install]
# WantedBy=timers.target
Set the parameter OnCalendar to the desired frequency. *-*-* 6:00 runs dnf-automatic at 6.00 AM everyday.
Note that you must set the custom configurations between these lines or the configurations will be discarded.
### Anything between here and the comment below will become the new contents of the file
[Timer]
OnCalendar=*-*-* 6:00
### Lines below this comment will be discarded
Apply the changes:
$ sudo systemctl daemon-reload
$ sudo systemctl restart dnf-automatic.timer
How to patch mission-critical RHEL workloads
While dnf-automatic is excellent for utility servers, jump hosts, or non-production labs, it is risky for mission-critical production databases or application servers.
Here's why:
- The "Bad Patch" Problem: If a patch contains a bug (regression),
dnf-automaticwill install it blindly. If that patch breaks your web server config, you will wake up to an outage. - Dependency Hell: An automatic update might upgrade a library (like Python or PHP) to a minor version that your custom application code doesn't support yet.
- Reboot Timing: Kernel updates require a reboot to take effect.
dnf-automaticdoes not reboot your server. If you automate reboots separately, you risk rebooting in the middle of a critical customer transaction.
So, do not set up dnf-automatic on enterprise, mission-critical RHEL fleets. For such production fleets, you need to do "Curated Patching". This is usually achieved using Red Hat Satellite or Ansible Automation Platform.
Red Hat Satellite (The Curator)
Satellite acts as a "middleman" between Red Hat and your servers.
- Content Views: You download updates to Satellite, but you don't release them to servers immediately.
- Lifecycle Environments: You "promote" updates to a Dev environment first. After a week of testing, you promote that exact same set of updates to Production. This guarantees that what you tested is exactly what you install in production.
Ansible (The Orchestrator)
Ansible handles the process of patching, which dnf-automatic cannot do. An Ansible playbook can:
- Remove the server from the Load Balancer.
- Stop the application gracefully.
- Run
dnf update. - Reboot the server.
- Wait for the server to come back online.
- Run a test script to verify the app is working.
- Add the server back to the Load Balancer.
Are you an aspiring DevOps engineer. Subscribe to Cloud Letters to get weekly insights for your DevOps career.