Neglecting Security Patches — A Risk Not Worth Taking
There’s a common reality in small DevOps teams: the server runs smoothly for months, then everyone forgets about security patch updates. When an incident occurs, looking back at the logs reveals a vulnerability that was publicly disclosed three months ago with a patch already available.
I’ve encountered this situation myself. My company still has a few servers running CentOS 7, and migrating to AlmaLinux is something I’ve already handled — but while waiting for the migration, I needed an automated security patching mechanism without having to SSH into each machine every week. After trying a few approaches, dnf-automatic is my most trusted choice for RHEL-based servers, especially CentOS Stream 9.
In this article, I’ll walk through the complete setup: installation, configuring it to apply only security updates, and enabling email notifications so you know what gets patched each day.
What is dnf-automatic and Why Use It?
dnf-automatic is the official tool from the DNF ecosystem, designed to automate package updates. Unlike running dnf update manually, it integrates directly with systemd timers — no need for custom cron jobs or complex wrapper scripts.
Advantages over other solutions:
- Granular control: choose to only download, only apply security updates, or update everything
- Built-in email notifications: no need to pipe output externally
- Systemd timer: runs on schedule, with logging and retry if the system is offline
- Random delay: prevents multiple servers from hitting the mirror repo simultaneously
I typically run it in apply security updates only mode — the safest option for production environments, avoiding the risk of a package update breaking the application.
Hands-On: Step-by-Step Installation and Configuration
Step 1: Install dnf-automatic
This package is available in the default CentOS Stream 9 repository, install it directly:
sudo dnf install -y dnf-automatic
Check the version and default configuration file:
dnf-automatic --version
cat /etc/dnf/automatic.conf
Step 2: Configure automatic.conf
The main configuration file is located at /etc/dnf/automatic.conf. Let’s walk through each important section:
sudo cp /etc/dnf/automatic.conf /etc/dnf/automatic.conf.bak
sudo nano /etc/dnf/automatic.conf
Settings to adjust in the [commands] section:
[commands]
# Download security updates only, don't apply immediately
# Use this for environments that require review first
# upgrade_type = security
# download_updates = yes
# apply_updates = no
# --- Or: Apply security updates automatically (recommended for production) ---
upgrade_type = security
download_updates = yes
apply_updates = yes
# Random delay up to 60 minutes — prevents all servers from updating at the same time
random_sleep = 3600
The [emitters] section — configure how notifications are sent:
[emitters]
# Send notifications via email (requires SMTP or local MTA)
emit_via = email
# Only send email when there are updates (avoids daily spam)
# available: always, never, only-if-updated
email_to = [email protected]
email_from = [email protected]
email_host = localhost
# If using a different port (e.g., Gmail SMTP):
# email_host = smtp.gmail.com
# email_port = 587
The [base] section — general settings:
[base]
debuglevel = 1
# Log path
# /var/log/dnf.log automatically captures dnf output
Step 3: Enable the systemd Timer
dnf-automatic comes with 3 different timers; choose the one that fits your needs:
# List available timers
ls /usr/lib/systemd/system/dnf-automatic*.timer
# dnf-automatic.timer — apply updates (use this one)
# dnf-automatic-download.timer — download only, don't apply
# dnf-automatic-install.timer — alias for the first one
Enable the timer for applying security updates:
# Enable and start the timer
sudo systemctl enable --now dnf-automatic.timer
# Check status
systemctl status dnf-automatic.timer
systemctl list-timers dnf-automatic*
Sample output when the timer is running correctly:
NEXT LEFT LAST PASSED UNIT
Wed 2026-07-02 06:42:15 JST 18h left Tue 2026-07-01 06:12:03 JST 5h ago dnf-automatic.timer
Step 4: Configure Email Notifications via Postfix (Local MTA)
If the server doesn’t have an MTA, install Postfix to send emails via a relay:
sudo dnf install -y postfix mailx
sudo systemctl enable --now postfix
Quick email test:
echo "Test from CentOS Stream 9" | mail -s "dnf-automatic test" [email protected]
If the server needs to relay through Gmail or an external SMTP server, add the relay configuration to /etc/postfix/main.cf:
# Add to the end of /etc/postfix/main.cf
relayhost = [smtp.gmail.com]:587
smtp_use_tls = yes
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
smtp_tls_CAfile = /etc/ssl/certs/ca-bundle.crt
# Create credentials file
echo "[smtp.gmail.com]:587 [email protected]:app-password" | sudo tee /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd
sudo chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
sudo systemctl restart postfix
Note: Gmail requires an App Password (not your main password). Go to Google Account → Security → App passwords to create one.
Step 5: Manual Test Before Enabling Automation
Always run a manual test once to confirm the configuration is correct:
# Run dnf-automatic immediately, without waiting for the timer
sudo dnf-automatic /etc/dnf/automatic.conf
# View detailed logs
sudo journalctl -u dnf-automatic -f
# Or trigger the timer immediately
sudo systemctl start dnf-automatic
Check the log to see if any updates were applied:
sudo cat /var/log/dnf.log | grep -E "(Upgraded|Installed|security)"
# View update history
sudo dnf history list | head -20
Step 6: Adjust the Schedule (Optional)
By default, the timer runs at 6 AM with a random delay. To change the time, override the timer:
sudo systemctl edit dnf-automatic.timer
Add the override content (for example, to run at 3 AM):
[Timer]
OnCalendar=
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=1800
sudo systemctl daemon-reload
sudo systemctl restart dnf-automatic.timer
Practical Tips from Real-World Experience
- Kernel updates: Security updates sometimes include the kernel. If you don’t want an auto-reboot, consider using
kernel-livepatchor excluding the kernel: addexclude=kernel*to the[base]section of automatic.conf and handle kernel updates manually during a maintenance window. - Stage first: For critical production systems, use
download_updates = yes, apply_updates = nomode — updates will be pre-downloaded, and you just need to rundnf update --cacheonlyduring the maintenance window. - Monitoring: Beyond email, you can add alerts via Slack/Telegram by creating a systemd service wrapper that sends a webhook after dnf-automatic completes.
- Multiple servers: For large fleets, I recommend combining with Ansible to push automatic.conf configurations uniformly, rather than SSH-ing into each machine.
Conclusion
Setting up dnf-automatic takes about 15 minutes but saves a significant amount of effort down the line. The most important thing is choosing the right upgrade_type = security — patching only security vulnerabilities without touching feature updates that could cause breaking changes.
Once enabled, I typically leave it alone for a few weeks before checking the logs again — email notifications will alert you if anything happens. This is the most practical approach for production servers where the team doesn’t have time for regular manual patching.

