Whether it's a bare-metal box or a fresh Ubuntu EC2 instance, every Linux server I put into production goes through the same hardening pass. This is that checklist, distilled from years of running systems that had to stay up.
1. Users & Access
- Never operate as root day-to-day. Create a sudo user and disable direct root login.
- Enforce SSH key authentication; disable password login entirely in
/etc/ssh/sshd_config. - Set a sane session timeout (
TMOUT) for idle shell sessions.
2. Firewall: Default Deny with UFW
Only expose what serves traffic
If a service doesn't need to be reachable from the internet — a database, an internal admin panel, Redis — it should be bound to 127.0.0.1 or a private VPC subnet only, never a public interface.
3. Automatic Security Updates
Unattended, unreviewed patching can break production, but unpatched production is worse. I enable automatic installation of security-only updates and review kernel/major package upgrades manually on a schedule.
4. Kernel & Network Hardening (sysctl)
These few lines close off classic spoofing and SYN-flood vectors that automated scanners probe for constantly.
5. Fail2ban & Intrusion Detection
fail2ban watches auth logs and temporarily bans IPs after repeated failed logins — SSH, but also anything else that logs failures (nginx basic auth, WordPress login, etc). Pair it with auditd for a proper audit trail of privileged commands.
6. File Permissions & Service Isolation
- Run each service (nginx, PHP-FPM, the app itself) as its own dedicated, unprivileged user.
- Never run application code as
www-datawith write access to its own source files. - Use
chmod 600on any file containing secrets (.env, private keys).
7. Logging & Monitoring
Centralize logs off-box — if a server is compromised, local logs can be tampered with or deleted. A simple forward to a log aggregator (or even a cheap syslog-to-S3 pipeline) preserves the evidence trail you'll need after an incident.