Enable Automatic Security Updates on a Django Server
Keep a Django server patched automatically: unattended-upgrades on Ubuntu and Debian or dnf-automatic on RHEL, security-only origins, service restarts, reboot windows, exclusions for PostgreSQL, and monitoring.
Problem statement
The operating system under your Django app (OpenSSL, the kernel, OpenSSH, Nginx, glibc, the system Python) receives security fixes every week. On a server someone logs into once a month, those fixes wait, sometimes for years. Many compromises of small production servers exploit vulnerabilities for which patches had long been available.
Automatic security updates close that gap with little effort. The concerns are real but manageable: an update restarting PostgreSQL mid-day, a library update that needs services restarted to take effect, and kernel updates that only apply after a reboot. This guide enables automatic security updates on Ubuntu, Debian, and RHEL-family servers and handles those concerns.
- Ubuntu and Debian: install
unattended-upgradesand enable the daily periodic job; keep the default security-only origins. - RHEL, Rocky, and AlmaLinux: install
dnf-automatic, setupgrade_type = securityandapply_updates = yes, enablednf-automatic.timer. - Make sure services using updated libraries are restarted (
needrestarton Ubuntu and Debian,dnf needs-restartingon RHEL). - Decide on reboots: automatic at a fixed low-traffic time, or alert when
/var/run/reboot-requiredexists and reboot manually. - Exclude packages you upgrade deliberately (often PostgreSQL) if restarts must be scheduled.
- Monitor that updates run and the server is not waiting for a reboot.
Step-by-step solution
1. Ubuntu and Debian: unattended-upgrades
sudo apt install unattended-upgrades apt-listchanges
sudo dpkg-reconfigure --priority=low unattended-upgrades
This writes /etc/apt/apt.conf.d/20auto-upgrades:
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";
The main configuration is /etc/apt/apt.conf.d/50unattended-upgrades. The defaults install from the security pocket only. Review and adjust a few options, ideally in a separate file so package upgrades do not conflict with your edits:
// /etc/apt/apt.conf.d/52unattended-upgrades-local
Unattended-Upgrade::Remove-Unused-Kernel-Packages "true";
Unattended-Upgrade::Remove-Unused-Dependencies "true";
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "04:30";
Unattended-Upgrade::Mail "ops@example.com";
Unattended-Upgrade::MailReport "only-on-error";
Unattended-Upgrade::Package-Blacklist {
"postgresql-";
};
Mail requires a working local mail setup; without one, use monitoring instead (step 5). Test the configuration:
sudo unattended-upgrade --dry-run --debug 2>&1 | tail -20
Runs are logged in /var/log/unattended-upgrades/.
2. RHEL-family: dnf-automatic
sudo dnf install dnf-automatic
# /etc/dnf/automatic.conf
[commands]
upgrade_type = security
apply_updates = yes
download_updates = yes
[emitters]
emit_via = stdio
sudo systemctl enable --now dnf-automatic.timer
systemctl list-timers | grep dnf-automatic
Newer versions of dnf-automatic (with dnf5) also support a reboot setting for automatic reboots when needed; check man dnf-automatic for your release. Excluded packages go in /etc/dnf/dnf.conf with excludepkgs=.
3. Restart services after library updates
Updating OpenSSL or glibc on disk does not change running processes; Gunicorn, Nginx, and PostgreSQL keep using the old, vulnerable copy in memory until restarted.
On Ubuntu and Debian, needrestart detects such processes after upgrades. In non-interactive runs it can restart affected services automatically:
# /etc/needrestart/conf.d/auto.conf
$nrconf{restart} = 'a';
Check manually:
sudo needrestart -b
On RHEL-family systems:
sudo dnf needs-restarting -s # services to restart
sudo dnf needs-restarting -r # is a reboot needed?
Restarting Gunicorn causes a brief interruption unless you run several instances or use socket activation; schedule automatic restarts for low-traffic hours or accept the brief blip in exchange for being patched.
4. Handle reboots
Kernel and some core library updates require a reboot. Options:
- Automatic reboot at a fixed time (as configured above). Suitable when a short nightly interruption is acceptable, or when you run several servers behind a load balancer and reboot times are staggered.
- Manual reboot with alerting: check for the flag file and alert:
test -f /var/run/reboot-required && cat /var/run/reboot-required.pkgs
Either way, confirm that everything comes back on its own after a reboot: systemctl is-enabled gunicorn celery-worker nginx postgresql should all report enabled.
5. Exclude what you upgrade deliberately
PostgreSQL minor updates restart the database. That is safe and recommended, but some teams prefer to apply them in a planned window. Excluding a package means you are now responsible for updating it; put a recurring task in your calendar and do not let excluded packages drift for months. The same applies to Docker Engine on hosts running containers.
6. Remember what this does not cover
- Python packages in your virtual environment (Django, requests, Pillow) are not managed by the OS package manager. Update them through your dependency process.
- Container images do not change on the host; rebuild them regularly on updated base images.
- Distribution upgrades (for example Ubuntu 22.04 to 24.04) are not automatic and need planning, since they change the system Python and other major versions.
Verification check:
systemctl list-timers | grep -E "apt-daily|dnf-automatic"
sudo tail -n 20 /var/log/unattended-upgrades/unattended-upgrades.log
apt list --upgradable 2>/dev/null | grep -i security | head
A day after enabling, the log should show a run, and pending security updates should be close to zero.
Explanation
Distribution security teams backport fixes into stable package versions, so security-only updates change very little besides the vulnerable code, which is why they are safe to automate. The package manager replaces files on disk; needrestart or needs-restarting makes running services load the fixed versions; a reboot applies kernel fixes. Automating all three turns patching from a task someone must remember into a background process with a known schedule and visible results.
Edge cases / notes
Several servers
Stagger update and reboot times so not every server restarts at once, or let your load balancer drain each server before its reboot.
Immutable infrastructure
If servers are rebuilt from images rather than updated in place, the equivalent is rebuilding the base image regularly and redeploying. Unattended updates still help between rebuilds.
When to automate this
Enable unattended updates in your provisioning (cloud-init or Ansible), ship the local configuration file with it, and add monitoring for "reboot required for more than N days" and "last successful update run older than two days".
Next steps
- Review the rest of host security with the Linux Server Hardening Checklist for Django.
- Keep Python dependencies patched with Automate Django Dependency and Security Updates.
- Cover the supply chain with the Django Dependency Security Checklist.
- Make sure reboots do not go unnoticed with Set Up Uptime Monitoring and Alerts for Django.
FAQ
Is it safe to enable automatic updates on a production Django server?
Security-only updates are low risk and much safer than leaving known vulnerabilities unpatched. Plan reboots and service restarts, and exclude packages you prefer to update manually.
Do automatic updates update Django itself?
No. Django and other Python packages in your virtual environment are managed separately, through your requirements and deploy process.
Why is my server still vulnerable after updates installed?
Running processes use the old libraries until restarted, and kernel fixes need a reboot. Use needrestart or dnf needs-restarting and reboot when required.
Should PostgreSQL be updated automatically?
Minor updates are safe but restart the database. Either allow them in a low-traffic window or exclude PostgreSQL and update it on a regular schedule.