Scope
Applies to any new Linux server intended to handle production traffic or production data: web application, API backend, WordPress site, internal tool, email server, jump host. Work top-to-bottom — items reference the guides for the “how”; this checklist is the “have I done it” layer.
Compliance references map only where they actually apply. They are not padded with vague mappings, and they are not legal advice — for a formal audit, use your certifying body’s control set as the authoritative source.
1. Before you provision
- Hosting region picked. EEA region by default when data processing falls under EU jurisdiction. GDPR: Article 44+.
- Provider DPA signed. Verify a Data Processing Agreement is published and executed before traffic flows. GDPR: Article 28.
- Backup target chosen and EEA-aware. Same region preferred;
encrypted at rest. See
postgres-backups. - Logging / monitoring stack chosen. Retrofitting log minimisation after launch is painful — decide now.
- Privacy posture decided. Worked through
privacy-by-design-server-build: log streams, retention windows, what is masked or hashed. GDPR: Article 25.
2. First-boot hardening
- Provider snapshot taken. Fresh image, pre-changes — restoring is faster than diagnosing.
- Per-human operator account created. Not
root, notubuntu, never a shared account. NIS2: 21(2)(i); ISO 27001: A.5.16. - SSH key-only authentication.
PasswordAuthentication no,PermitRootLogin no,AllowGroups sudo(Debian) orwheel(RHEL). Seessh-hardening. NIS2: 21(2)(e),(j); ISO 27001: A.8.5. - Modern SSH keys in use. Ed25519 with passphrase; the
authorized_keysentries carry dated comments. - OS baseline applied. Run through
ubuntu-baselineorrhel-baseline. NIS2: 21(2)(g). - Host firewall enabled.
ufworfirewalld, default-deny, explicit allows. NIS2: 21(2)(e); ISO 27001: A.8.20. - Automatic security updates configured. With a documented reboot policy. NIS2: 21(2)(c); ISO 27001: A.8.8.
- Time sync working.
chronyenabled and reporting healthy sources. -
auditdenabled with baseline ruleset and forwarded off-host. NIS2: 21(2)(b); ISO 27001: A.8.16. - AppArmor / SELinux in enforcing mode. Verified, not disabled.
- Unused services removed.
cups,bluetooth,avahi,postfix(if not sending mail), etc.
3. TLS and edge
- Certificate issued. ECDSA P-256 via Let’s Encrypt by default. NIS2: 21(2)(h); ISO 27001: A.8.24.
- Nginx TLS baseline applied. TLS 1.2/1.3 only, OCSP stapling,
HSTS, modern ciphers — see
nginx-tls-2026. - HSTS deployed without
preloadinitially. Addpreloadonly after stable operation and after auditing every subdomain. - Rate limiting in place on authentication and signup endpoints.
See
nginx-ratelimit. - External TLS scan passed. SSL Labs grade A or A+; document any deviation.
4. Data tier
- PostgreSQL hardened, if used.
postgres-auditreturns zero FAILs. Seepostgresql-hardening. NIS2: 21(2)(e),(h). - Redis hardened, if used.
redis-checkreturns zero FAILs. Seeredis-hardening. - Application database role is not superuser. Least-privilege grants only.
- TLS between application and database. Mandatory across hosts; recommended on loopback.
5. Backups
- Daily logical + weekly base backup scheduled. See
postgres-backups. NIS2: 21(2)(c); ISO 27001: A.8.13. - Off-site, encrypted backup verified. restic / borg to an EEA-region S3-compatible endpoint. GDPR: Article 32(1)(c).
- Backup passphrase stored off-server. Password manager that survives loss of the production server; at least two humans know it.
- Restoration drill performed. Documented date and time-to-restore. A backup that has never been restored is not a backup.
6. Application layer
- Production secrets in a secrets manager. Not in
.envfiles on disk or in environment variables visible viaps. - Application logs do not contain personal data in literals. Verified against a representative log sample.
- Health endpoint not publicly enumerable. Internal IP allowlist or token-gated.
- Stack traces not exposed in production error pages.
7. Monitoring and alerting
- At least one alert channel a human actually reads. Email, Slack, PagerDuty — does not matter which.
- Alert on SSH authentication failures above threshold.
- Alert on
sudouse. - Alert on backup failure. Silent crontab failure is the classic gap.
- Alert on TLS certificate expiry < 21 days. Safety net even with auto-renewal.
- Out-of-band incident comms channel agreed. A Signal group, a pager number — anything that does not depend on the systems an incident may have taken down.
8. Documentation and final checks
- Baseline applied is documented. Which guides, which version, which date. NIS2: 21(2)(a).
- Asset inventory updated. Hostname, role, region, owner. ISO 27001: A.5.9.
- Processor / supplier register updated. Hosting, CDN, backup target, monitoring SaaS — with DPA references. GDPR: Article 28.
- Incident response contacts current. On-call rotation, escalation, out-of-band comms channel.
- Privacy notice reflects what this server processes. Including the log retention period.
-
lynis audit systemor equivalent run. WARNINGs reviewed and either fixed or documented as accepted. - Post-hardening snapshot taken. Restore-ready baseline.
Notes
The aim of this list is “defensible at launch” — not “perfect”. The larger value is in quarterly re-runs against the same items: plugin drift, retention drift, key drift, and account creep are caught by the review, not by the initial setup.
If you run more than a handful of servers, treat this list as the content and a configuration management tool — Ansible, NixOS, salt — as the enforcement. A list of checked boxes does not survive the third new server you build at three in the morning.