Every new server requires basic security hardening before any application is deployed on it. This checklist covers every level: from system updates to monitoring and backups.
Section 1: System Updates
apt update && apt upgrade -y
apt install unattended-upgrades -y
dpkg-reconfigure -plow unattended-upgrades # enable automatic security updates
Section 2: Users and sudo
useradd -m -s /bin/bash admin
usermod -aG sudo admin
passwd admin
# Check that root login by password is disabled
passwd -l root
Section 3: SSH Hardening
# Already covered in detail in the SSH Hardening article
# The essentials:
Port 2222
PermitRootLogin no
PasswordAuthentication no
AllowUsers admin
Section 4: Firewall (UFW)
apt install ufw -y
ufw default deny incoming
ufw default allow outgoing
ufw allow 2222/tcp # SSH (your port)
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
ufw status verbose
Section 5: fail2ban
apt install fail2ban -y
systemctl enable --now fail2ban
Section 6: Time Synchronization
apt install chrony -y
systemctl enable --now chrony
timedatectl status
Section 7: Monitoring and Audit
apt install auditd audispd-plugins -y
systemctl enable --now auditd
apt install logwatch -y
Full 30-Point Checklist
| # | Action | Priority |
|---|---|---|
| 1 | apt update && apt upgrade | Critical |
| 2 | Enable unattended-upgrades | High |
| 3 | Create a non-root user | Critical |
| 4 | Set up SSH keys | Critical |
| 5 | Change the SSH port | Medium |
| 6 | PermitRootLogin no | Critical |
| 7 | PasswordAuthentication no | Critical |
| 8 | Set up UFW/nftables | Critical |
| 9 | Install fail2ban | High |
| 10 | Set up NTP (chrony) | Medium |
| 11 | Set up hostname and /etc/hosts | Low |
| 12 | Install auditd | Medium |
| 13 | Set up logrotate | Medium |
| 14 | Install ClamAV (if needed) | Medium |
| 15 | Set up rkhunter | Medium |
| 16 | Set up backups | Critical |
| 17 | Check open ports | High |
| 18 | Set up swap (if missing) | Medium |
| 19 | Set up sysctl hardening | Medium |
| 20 | Enable HSTS for the web server | High |
| 21 | Set up SSL (Let's Encrypt) | Critical |
| 22 | Disable unused services | Medium |
| 23 | Check SUID files | Medium |
| 24 | Set up uptime monitoring | High |
| 25 | Set up email alerts | Medium |
| 26 | Document the configuration | Medium |
| 27 | Set up CrowdSec or OSSEC | Medium |
| 28 | Check config file permissions | High |
| 29 | Set up SSH key rotation | Low |
| 30 | Conduct a penetration test | Medium |
Sysctl hardening — an additional layer of kernel protection. Add this to
/etc/sysctl.d/99-security.conf:
net.ipv4.tcp_syncookies = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
kernel.randomize_va_space = 2
kernel.dmesg_restrict = 1
Apply: sysctl --system
Common Errors During Initial Setup
| Symptom | Cause |
|---|---|
| The SSH connection drops right after ufw enable | Forgot to allow the SSH port (ufw allow 2222/tcp) before enabling — the firewall activated and cut off the only access channel |
| sshd: no hostkeys available after changing the port | Port was changed in sshd_config but the service was never restarted, or there is a typo in the config and sshd does not start at all |
| fail2ban: Fail to start jail 'sshd'... Permission denied | fail2ban's backend is not configured to read the systemd journal, or logpath in jail.local points to a file that no longer exists after the SSH port changed |
| unattended-upgrades: no packages found | The needed Origins-Pattern lines in /etc/apt/apt.conf.d/50unattended-upgrades are not uncommented for this particular distribution |
| chronyd: Could not synchronise | Outbound UDP port 123 is blocked by the provider's firewall or by ufw itself, if the default deny outgoing rule is set too strictly |
When This Checklist Is Not Enough
- The server stores payment data or falls under a formal standard (PCI DSS and similar) — the 30 steps provide basic hygiene, not a substitute for a formal compliance audit
- You need protection against application-level attacks — SQL injection, XSS, and logic flaws are not covered by this checklist, which only addresses the OS and the network perimeter
- The infrastructure spans dozens of servers — repeating 30 steps manually on each one does not scale; automate it with Ansible or Terraform
- You need protection against a volumetric DDoS attack — fail2ban and ufw are effective against targeted brute-forcing but powerless against an attack on the channel's bandwidth; that requires protection at the provider or CDN level
Verifying the Result
After completing all 30 items, run a quick manual audit — it is easy to check off a checklist item without actually confirming the setting works. A deeper look at SSH hardening and event auditing is in the articles on SSH hardening and on monitoring with auditd.
# Which ports are actually listening on the server
sudo ss -tlnp
# Firewall rules match what is expected
sudo ufw status verbose
# The fail2ban jail is active and banning brute-force attempts
sudo fail2ban-client status sshd
# Key services are enabled to start on boot
sudo systemctl is-enabled fail2ban auditd chrony