Security updates on Ubuntu and Debian: automate without breaking production
Most successful attacks don't use clever new techniques. They use a vulnerability that was published, patched, and never installed. Keeping a server up to date is the cheapest security measure there is, yet it is the one small teams postpone the most, out of fear of breaking production.
Here is how to automate what can be automated, and keep an eye on the rest.
Three layers to keep up to date
A typical server has three layers, and each one is updated differently:
| Layer | Examples | How it is updated |
|---|---|---|
| Operating system | kernel, OpenSSH, OpenSSL, libc | apt |
| Runtimes and services | Node.js, Python, PHP, Nginx, PostgreSQL | apt, vendor repositories, Docker images |
| Application dependencies | npm, pip, composer packages | your project, at deploy time |
Automatic system updates only cover the first layer, and part of the second.
Layer 1: automatic security updates
On Ubuntu and Debian, unattended-upgrades installs security patches every day:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
Keep the default: security updates only. Feature upgrades are where surprises come from.
Two settings worth reviewing in /etc/apt/apt.conf.d/50unattended-upgrades:
Unattended-Upgrade::Mailto receive a summary;Unattended-Upgrade::Automatic-Reboot, usuallyfalseon production, with a planned reboot window instead.
The reboot problem
A patched kernel or libc is not active until the server (or the service) restarts. Check if a restart is pending:
cat /var/run/reboot-required 2>/dev/null
sudo needrestart -r l
Plan a weekly maintenance window rather than letting pending restarts pile up for months.
Layer 2: runtimes and services
Language runtimes installed from vendor repositories or by hand are often forgotten. A Node.js or PHP version that reached end of life no longer receives security fixes at all.
List what you run and the version:
node -v; python3 --version; php -v; nginx -v; psql --version
Then compare with the supported versions published by each project.
Layer 3: application dependencies
This is where most web compromises start: a framework or library with a known critical CVE, deployed months ago and never touched since. apt will never update it.
Each ecosystem has an audit command (npm audit, pip-audit, composer audit), but they only help if someone runs them, on every server, regularly.
A routine that works for small teams
- Every day: automatic OS security updates.
- Every day: a check of installed versions against public vulnerability databases, with the fixed version to install.
- Every week: a maintenance window to restart what needs it and update dependencies flagged as critical.
- Every month: review runtimes reaching end of life.
Automating step 2
Step 2 is the hardest to do by hand across several servers. FounderMode checks system packages and npm, Python and PHP dependencies against public CVE databases every day, and lists in its morning report what to update and to which version, alongside intrusion attempts and server health. Try it free for 14 days.