A server is configured once and then lives on by itself. A month later some packages are waiting to be upgraded, the certificate has drifted within three weeks of expiry, an extra line appeared in the SSH config after something unrelated was installed, and the jail that is supposed to watch logins has stopped without anyone noticing. None of this shouts. It all just quietly stops being true.

Below are twelve things worth checking regularly, with the commands. The round takes about fifteen minutes. The order runs from what people get in through to what breaks on its own.

SSH: five lines

What to look at is not the file but the effective configuration: it is assembled from /etc/ssh/sshd_config, the whole sshd_config.d directory and the build defaults.

sudo sshd -T | grep -iE 'permitrootlogin|passwordauthentication|permitemptypasswords|maxauthtries|x11forwarding'

What we want to see: permitrootlogin no, passwordauthentication no, permitemptypasswords no, maxauthtries at 3–4, and x11forwarding no. The first three are the door; the last two are its hinges — a cap on attempts per connection, and graphics forwarding a server has no use for.

Two caveats, both easy ways to lock yourself out. First: do not turn off password login and root login until you have confirmed your key works — in a separate session, without closing the current one. If you are reading this while logged in as root with a password, those two lines will shut out precisely you.

Second: file order. OpenSSH takes the first value it meets for a parameter, and files in sshd_config.d are read alphabetically. Cloud images usually ship a 50-cloud-init.conf in there, and it will override your own file if you named it something like 90-hardening.conf. Put your settings under a lower number — 00- or 10-. And check the result with sshd -T rather than by reading the file.

Firewall: one check

sudo ufw status verbose

Worth remembering that this output shows intentions, not results. A rule can sit in the list and close nothing: UFW itself is disabled, the port is served by something Docker published (it writes its own rules into iptables below UFW), or traffic simply reaches the server by another path. So it pays to compare the rule list against what is actually listening outward:

sudo ss -tulpn | grep -v '127.0.0.1\|::1'

fail2ban: two checks

It is not enough for the service to be running — there has to be a working jail for SSH as well:

sudo fail2ban-client status
sudo fail2ban-client status sshd

The most common trouble here is a silent jail. On Debian 12 and recent Ubuntu there may be no /var/log/auth.log at all: rsyslog is not installed and the records live only in journald. A jail with the stock logpath then starts, reports itself as active, and bans nobody ever. The cure is to point it at the systemd journal:

printf '[sshd]\nenabled = true\nbackend = systemd\n' | sudo tee /etc/fail2ban/jail.d/sshd-systemd.local
sudo systemctl restart fail2ban

The sign that catches this without digging through configs: in fail2ban-client status sshd both "Currently failed" and "Total failed" sit at zero while the journal clearly has failed logins in it.

Updates: three checks

Separately: how many packages are waiting at all, how many of those are security ones, and whether the system is asking for a reboot after a kernel upgrade.

sudo apt update
apt list --upgradable 2>/dev/null | grep -c -- '-security'
ls /var/run/reboot-required 2>/dev/null && echo 'reboot required'

The third check is whether automatic security updates are switched on, so that the first two do not turn into a monthly ritual:

systemctl status unattended-upgrades --no-pager
cat /etc/apt/apt.conf.d/20auto-upgrades

That file should carry a 1 on both lines. If the package is missing: sudo apt install unattended-upgrades && sudo dpkg-reconfigure -plow unattended-upgrades.

Certificates: one check

A Let's Encrypt certificate lives ninety days, and renewal usually works by itself right up to the day it does not. What to look at is not the registrar's panel but what the server actually serves:

echo | openssl s_client -connect localhost:443 -servername example.com 2>/dev/null | openssl x509 -noout -enddate
sudo certbot renew --dry-run

The first command gives the expiry date of the certificate served for that name; the second checks that renewal will go through without waiting for the real deadline. It is worth walking through every name the server handles, not just the main domain: the one people forget is the subdomain added later.

Disk: one check

df -h
df -i

Two commands, because space and file records run out independently of each other. On most servers the second hits its ceiling first — millions of small files in PHP sessions or an application cache give an IUse% of 100 with gigabytes still free. We have a separate piece covering both cases in detail.

What is wrong with this list

The list is correct, but it has one property: it requires somebody to remember it. Fifteen minutes a week is not much, as long as there is only one server and as long as there is a reason. After two quiet months the round gets skipped, and the news that the jail stopped arrives from somebody else's logs.

So we put exactly these twelve checks into a separate free panel — Arcivéo FREE. It installs with one command, runs on your own server, sends nothing to us, and needs no registration. A collector runs from cron every five minutes and writes the same things into a local database: SSH logins and refused connections, the state of UFW and fail2ban, pending security updates, certificate deadlines, disk space. History is seven days, the interface is in 34 languages. The price is zero, including on a work server: the licence allows installing the panel on your own machines, company machines included, and does not allow reselling it or running it as a service for somebody else's servers.

The free edition's stack is modest: it installs and enables UFW and fail2ban, and from there it shows state. ModSecurity, Suricata, AIDE and the other modules are not in it, those belong to the paid one. But to stop carrying the weekly round in your head, it is enough. What the same data looks like on the full panel is on the demo pages below.