We manage Linux servers for every web application and SaaS product we build. Security isn't a feature we add at the end — it's the first thing we configure before any application code touches the server.
This checklist covers the essential hardening steps we apply to every production server. It's opinionated, practical, and based on years of keeping servers secure without making them unusable for the development team.
SSH Hardening: Your First Line of Defense
SSH is how you access the server. If it's misconfigured, nothing else matters.
Disable root login. Create a dedicated admin user and use sudo. This isn't just security theater — it creates an audit trail of who did what. Disable password authentication entirely. Use SSH keys only. A strong password is still weaker than a 4096-bit RSA key, and key-based auth eliminates brute-force attacks completely.
Change the default SSH port from 22 to something non-standard. This doesn't stop a determined attacker, but it eliminates 99% of automated scanning noise from your logs. Configure idle timeout to disconnect inactive sessions after 10 minutes. Limit SSH access to specific IP addresses using AllowUsers or firewall rules when possible.
Firewall Configuration with UFW or nftables
Default deny all incoming traffic. Then explicitly allow only what your application needs: ports 80 and 443 for web traffic, your custom SSH port from trusted IPs, and nothing else unless specifically required.
If your application uses a database, that database port should never be exposed to the public internet. If another service needs database access, use an internal network, SSH tunnel, or VPN. We see exposed PostgreSQL and MySQL ports on production servers more often than we'd like to admit — it's one of the most common and dangerous misconfigurations.
Rate-limit SSH connections to prevent brute-force attempts. Even with key-only authentication, rate limiting reduces log noise and protects against zero-day SSH vulnerabilities.
Automated Security Updates
Unpatched software is the number one cause of server compromises. Configure unattended-upgrades (Debian/Ubuntu) or dnf-automatic (RHEL/Fedora) for security patches.
Our configuration applies security updates automatically within 24 hours of release. Non-security updates are applied manually during maintenance windows to avoid unexpected breaking changes. We also monitor CVE databases for vulnerabilities in the specific software stack each server runs. A critical vulnerability in nginx or OpenSSL requires immediate action, not waiting for the next update cycle.
Keep the kernel updated. Kernel vulnerabilities are rare but catastrophic when exploited. Enable automatic reboot after kernel updates during low-traffic hours, or use livepatch if downtime is not acceptable.
Application-Level Security
The server is only half the equation. The application running on it needs its own security measures.
Run the application as a dedicated non-root user with minimal permissions. The web server process should only have read access to application files and write access to specific directories (uploads, logs, temp). Configure proper file permissions: application code should be owned by a deploy user and readable (not writable) by the application user.
Use environment variables for secrets — never commit database passwords, API keys, or encryption keys to version control. We use a secrets manager or encrypted environment files with strict file permissions (600, owned by the application user). Set up proper HTTP security headers: Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Strict-Transport-Security. These headers prevent entire categories of attacks with minimal configuration effort.
Monitoring and Alerting
Security without monitoring is hope-based security. You need to know when something unusual happens.
Our minimum monitoring setup includes: fail2ban for automatic IP blocking after failed authentication attempts, log monitoring for unusual patterns (multiple 404s, authentication failures, unexpected process execution), resource monitoring for CPU, memory, and disk usage anomalies that might indicate cryptomining or other compromise, and file integrity monitoring for critical system files and application binaries.
Alerts go to a dedicated channel that gets checked. An alert that nobody reads is worse than no alert — it creates false confidence. We configure tiered alerting: critical issues (server down, disk full, suspected intrusion) page immediately; warnings (high CPU, unusual traffic patterns) aggregate into daily summaries.
Backup and Recovery
Backups are security. Ransomware, accidental deletion, and catastrophic failures all have the same solution: restore from a known-good backup.
Our backup strategy: full database backups every 6 hours, application file backups daily, configuration backups after every change, and backup verification tests monthly. Store backups off-server. A backup on the same machine it's backing up isn't a backup — if the server is compromised, the backups are too. We use encrypted off-site storage with 30-day retention.
Test your recovery process. A backup you've never restored is a backup you can't trust. We run quarterly recovery drills where we restore a full application stack from backups to a fresh server and verify everything works.
Server security isn't a one-time setup — it's an ongoing practice. The checklist above covers the essentials, but every application has specific security requirements based on the data it handles and the threats it faces. If you need help securing your Linux servers or want a professional audit of your current setup, we're here to help.
Have a project in mind?
Get a clear plan and honest estimate within 24 hours. No commitment, no sales pitch.
Start a Conversation