A freshly installed Linux server is built to be usable, not to be secure. Within minutes of getting a public IP address it will be probed by automated scanners looking for weak passwords and outdated software. Linux server hardening is the process of reducing that exposure: removing what you do not need, locking down what you do, and making sure you will notice if something goes wrong. The 20 steps below apply to Ubuntu, Debian and RHEL-family distributions such as AlmaLinux and Rocky Linux; commands are shown for both where they differ.
Linux server hardening foundations
1. Start from a supported release
Use a long-term support release that will receive security updates for years, and note its end-of-life date. Hardening an unsupported OS is wasted effort.
2. Apply all updates
sudo apt update && sudo apt full-upgrade -y # Debian/Ubuntu
sudo dnf upgrade -y # RHEL family
3. Enable automatic security updates
Use unattended-upgrades on Debian/Ubuntu or dnf-automatic on RHEL-family systems, configured for security updates at minimum. Decide separately how reboots will be handled.
4. Set the hostname, time zone and time sync
Accurate time matters for logs, certificates and authentication. Check with timedatectl and make sure NTP synchronisation is active (systemd-timesyncd or chrony).
Users and authentication
5. Create a named admin user
sudo adduser alice
sudo usermod -aG sudo alice # Debian/Ubuntu
sudo usermod -aG wheel alice # RHEL family
Every administrator gets their own account so actions can be traced.
6. Use SSH keys, not passwords
Generate a key on your own computer with ssh-keygen -t ed25519 and copy it with ssh-copy-id alice@server. Test that key login works before the next step.
7. Harden the SSH daemon
Create /etc/ssh/sshd_config.d/00-hardening.conf (supported on current releases). For most options sshd uses the first value it reads, and these files load in alphabetical order, so a low number stops files like 50-cloud-init.conf from overriding you:
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
LoginGraceTime 30
AllowUsers alice
Validate with sudo sshd -t, then reload the service. Keep your current session open until you have confirmed a new one works.
8. Lock or remove unused accounts
List accounts with login shells and remove those not needed. Lock the root password if you use sudo exclusively: sudo passwd -l root.
9. Configure sudo carefully
Avoid NOPASSWD for humans. Edit rules only with visudo, which checks syntax before saving.
Network exposure
10. Enable a host firewall with default deny
# Ubuntu/Debian with UFW
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80,443/tcp
sudo ufw enable
# RHEL family with firewalld
sudo firewall-cmd --permanent --add-service={ssh,http,https}
sudo firewall-cmd --reload
11. Restrict administrative ports by source IP
Where practical, allow SSH only from your office IP or VPN, for example sudo ufw allow from 203.0.113.10 to any port 22 proto tcp. Our What is my IP tool shows the address to allow.
12. Bind internal services to localhost
Databases and caches that only the local application uses should not listen publicly. In MySQL or MariaDB set bind-address = 127.0.0.1; in Redis, bind 127.0.0.1 ::1. Check what is listening with sudo ss -tulpn, and verify from outside with our port checker.
13. Install brute-force protection
Fail2ban watches logs and temporarily blocks IPs with repeated failed logins:
sudo apt install fail2ban # or: sudo dnf install fail2ban (EPEL)
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
Reduce the attack surface
14. Remove unneeded packages and services
Review running services with systemctl list-units --type=service --state=running. Disable anything not required, such as print services or unused mail daemons: sudo systemctl disable --now cups.
15. Apply sensible kernel network settings
Add to /etc/sysctl.d/99-hardening.conf and apply with sudo sysctl --system:
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
Only set ip_forward if the server routes traffic, for example for Docker or a VPN; those tools manage it themselves.
16. Keep mandatory access control enabled
SELinux (RHEL family) and AppArmor (Ubuntu, Debian) confine services even if they are compromised. Resist the common advice to disable them when something breaks; fix the policy instead. Check with getenforce or sudo aa-status.
17. Tighten file permissions and mount options
Make sure sensitive files are not world-readable, for example application config files with database passwords should be 640 and owned by the application user. Where you have a separate /tmp partition, mount it with noexec,nosuid,nodev. Find world-writable files with sudo find / -xdev -type f -perm -0002.
Visibility and recovery
18. Configure logging and keep logs off the box
Ensure logs persist across reboots (journald Storage=persistent) and are rotated. Forward important logs to a separate log server or service so an attacker cannot erase their tracks.
19. Audit and scan regularly
Tools such as Lynis perform an automated security audit and suggest improvements. auditd can record changes to sensitive files such as /etc/passwd and /etc/sudoers. The CIS Benchmarks provide a comprehensive reference if you need formal compliance.
sudo apt install lynis
sudo lynis audit system
20. Back up and monitor
Hardening reduces risk; it does not remove it. Keep tested, offsite backups and monitor for disk, load and unusual login activity so incidents are spotted quickly.
Document your baseline
Record every change you make so it can be repeated on the next server, ideally as an Ansible playbook or similar configuration management script. A consistent, automated baseline is far more reliable than hand-hardening each machine. This is part of the routine work in our server management service.
Key takeaways
- Linux server hardening starts with updates, key-only SSH and a default-deny firewall.
- Expose only the ports you need; keep databases and caches on localhost or a private network.
- Remove unused software, keep SELinux or AppArmor on, and tighten permissions.
- Log centrally, audit regularly with tools like Lynis, and automate your baseline.