Linux server hardening reduces the number of ways an attacker can enter a system, limits what a compromised account or service can do, and improves the chance that suspicious activity will be detected. It is not a one-time command: it is a repeatable process of patching, reducing exposure, controlling access, monitoring, and testing recovery.
This practical checklist targets Ubuntu Server 22.04 and 24.04. Most principles also apply to Debian and other Linux distributions, but package names, service names, firewall tools, and configuration paths can differ. Keep a provider console session and a tested backup available before changing remote access or firewall rules.
Linux server hardening checklist
- Inventory the system and back it up.
- Install security updates and use a supported release.
- Remove unused packages, services, accounts, and ports.
- Use named administrator accounts and least privilege.
- Protect SSH with keys and safe configuration testing.
- Apply a default-deny host firewall policy.
- Keep AppArmor enabled and review confinement.
- Harden service permissions and secrets.
- Enable useful logging and review authentication events.
- Protect data in transit and at rest.
- Monitor exposure and vulnerability status.
- Test backups and recovery regularly.
1. Inventory and back up before hardening
Start by recording the operating-system version, listening services, enabled service units, storage, and current firewall rules. This establishes a baseline and helps prevent accidental lockouts.
cat /etc/os-release uname -r sudo ss -tulpn sudo systemctl --type=service --state=running sudo ufw status verbose lsblk -f
Create a backup or provider snapshot and confirm how it will be restored. A snapshot alone is not a recovery plan: databases and application data may require application-consistent backups. Keep at least one backup isolated from the server being protected.
2. Patch the operating system
Security updates close known vulnerabilities. On Ubuntu, review and install available updates with:
sudo apt update apt list --upgradable sudo apt upgrade
Ubuntu Server normally includes unattended-upgrades for automatic security updates. Confirm its timers and recent activity rather than assuming it is working:
dpkg -l unattended-upgrades systemctl list-timers 'apt-daily*' sudo unattended-upgrade --dry-run --debug
Automatic updates still need operational planning. Monitor failed updates, schedule reboots when required, and test important applications after patching. Do not keep an end-of-life Linux release exposed to the internet.
3. Reduce the attack surface
Every unnecessary network service, package, repository, and account adds maintenance work and potential exposure. Compare listening ports with the services the server is actually intended to provide.
sudo ss -tulpn sudo systemctl list-unit-files --state=enabled sudo apt-mark showmanual sudo lastlog
Disable an unnecessary service only after identifying why it is installed:
sudo systemctl disable --now SERVICE_NAME
Remove unused software through the package manager, not by deleting files manually. Avoid untrusted third-party repositories, and keep an inventory of repositories that remain enabled.
4. Use least privilege
Administrators should use individual named accounts and elevate privileges only when necessary. On Ubuntu, add an administrator to the sudo group:
sudo adduser adminuser sudo usermod -aG sudo adminuser id adminuser
Do not grant blanket NOPASSWD:ALL access as a routine hardening step. If automation needs elevated commands, create a narrowly scoped sudo rule and use visudo to validate it. Review dormant users, service accounts, group membership, and scheduled jobs regularly.
Related guide: How to add a user to a group in Linux.
5. Harden SSH without locking yourself out
Create the SSH key on the administrator’s trusted computer, not on the server. Copy only the public key to the server and protect the private key locally.
ssh-keygen -t ed25519 ssh-copy-id adminuser@SERVER_IP
Keep the current SSH session open and verify key login in a second terminal. Then create a separate configuration snippet:
sudoedit /etc/ssh/sshd_config.d/99-hardening.conf
PermitRootLogin no PubkeyAuthentication yes PasswordAuthentication no KbdInteractiveAuthentication no MaxAuthTries 3 LoginGraceTime 30
Before reloading SSH, validate the configuration and inspect the effective values:
sudo sshd -t sudo sshd -T | grep -E 'permitrootlogin|passwordauthentication|kbdinteractiveauthentication|maxauthtries|logingracetime' sudo systemctl reload ssh
Disable password authentication only after every required administrator has a tested key and an emergency access method. Changing port 22 can reduce log noise, but it is not a substitute for keys, access controls, patching, or a firewall.
Related guide: Set up SSH key-based authentication.
6. Configure a default-deny firewall
Ubuntu’s default host-firewall tool is UFW. Add the SSH rule before enabling the firewall, especially on a remote server. Replace the example source network with the administrator’s actual trusted range when practical.
sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable sudo ufw status numbered
203.0.113.0/24 is documentation-only address space; do not copy it as a real allowlist. If the administrator’s address changes frequently, use an appropriate VPN, bastion host, or carefully rate-limited SSH policy. Open only ports required by the server’s role.
Related guide: Configure a firewall with UFW.
7. Keep AppArmor enforcing policy
AppArmor supplements normal Unix permissions by restricting what profiled applications can access. Ubuntu installs and loads AppArmor by default. Check its state and investigate profiles that unexpectedly run in complain mode:
sudo aa-status systemctl status apparmor --no-pager
Do not disable AppArmor simply to make an application work. Review the audit denial, confirm the application requirement, and adjust a local profile deliberately.
8. Protect services, files, and secrets
- Run network applications as dedicated unprivileged users.
- Keep configuration files and private keys readable only by the accounts that need them.
- Store application secrets outside source code and public web directories.
- Use separate database accounts with only the required privileges.
- Do not expose database, cache, or administration ports publicly unless the design requires it.
- Review container and orchestration privileges; a container is not automatically a security boundary.
Check sensitive-file permissions explicitly:
sudo find /etc/ssh -maxdepth 1 -type f -printf '%m %u:%g %pn' sudo find /home -xdev -type f -name authorized_keys -printf '%m %u:%g %pn'
9. Log and review security events
Hardening without monitoring leaves attacks and configuration failures invisible. Review authentication failures, sudo use, service failures, and firewall events. On Ubuntu, useful starting points include:
sudo journalctl -p warning..alert --since today sudo journalctl -u ssh --since today sudo grep -E 'Failed password|Invalid user|sudo:' /var/log/auth.log | tail -100 sudo ufw logging low
Centralize logs for important systems so an attacker cannot erase the only copy. Set alerts for repeated authentication failures, unexpected new listeners, low disk space, disabled security services, and failed backups. Retention should match operational and compliance needs.
10. Encrypt traffic and protect stored data
Use current TLS configurations for public services and automate certificate renewal. Restrict plaintext management protocols such as Telnet and FTP; prefer SSH, SFTP, and HTTPS. Encryption at rest can protect disks and backups, but keys must be stored and recoverable securely.
11. Recheck exposure continuously
Repeat the listening-port and service inventory after application changes. Subscribe to security notices for the operating system and critical software. Scan from outside the server’s network boundary as well as locally, because cloud firewalls, load balancers, NAT, and host rules can expose different surfaces.
sudo ss -tulpn sudo ufw status verbose systemctl --failed sudo apt update apt list --upgradable
12. Test recovery
Backups are useful only when they can be restored. Perform scheduled restore tests, document recovery time, and verify that databases, uploaded files, configuration, encryption keys, and DNS or infrastructure settings are included. Protect backup credentials separately from production credentials.
Practices this guide intentionally avoids
- Disabling IPv6 globally: this can break applications and does not replace proper firewall rules.
- Blocking every ICMP packet: ICMP supports diagnostics and network functions; filter deliberately instead.
- Disabling irqbalance: this is a performance setting, not a general security control.
- Blindly remounting
/tmpor linking/var/tmpto it: mount changes can break workloads and should be designed for the specific system. - Using a nonstandard SSH port as the main defence: it reduces automated log noise but does not prevent targeted access.
- Copying a private key from the server: generate private keys on a trusted client and never expose them through terminal output.
Official references
- Ubuntu Server security suggestions
- Ubuntu automatic updates
- Ubuntu OpenSSH server documentation
- OpenSSH
sshd_configmanual - Ubuntu firewall documentation
- Ubuntu AppArmor documentation
Conclusion
A hardened Linux server has fewer exposed services, timely security updates, controlled administrative access, tested SSH and firewall rules, enforced application boundaries, useful monitoring, and recoverable backups. Apply changes incrementally, validate each one, and keep an emergency access path available while modifying a remote production system.
Related Linux news: Desktop Linux is attracting more users, but recent statistics need careful interpretation. Read our analysis of whether Linux desktop market share really crossed 10% in North America.











Comments