Self-Hosting Security Checklist
Why a Security Checklist?
When you self-host, you’re responsible for securing everything. There’s no cloud provider handling patches, no managed firewall, no security team. A misconfigured server gets compromised — often within hours of being exposed to the internet.
This checklist covers every layer of security for a self-hosted server. Work through it from top to bottom. The items are ordered by impact — do the top ones first, they block the most common attacks.
The Checklist
SSH Hardening (Critical)
SSH is the primary target for automated attacks. Thousands of bots scan the internet for weak SSH configurations.
-
Disable password authentication — use key-based auth only
# /etc/ssh/sshd_config PasswordAuthentication no PubkeyAuthentication yesSee SSH Setup for the complete guide.
-
Disable root login over SSH
# /etc/ssh/sshd_config PermitRootLogin no -
Use Ed25519 keys (not RSA)
ssh-keygen -t ed25519 -C "[email protected]" -
Change the default SSH port (optional but reduces noise)
# /etc/ssh/sshd_config Port 2222 -
Install and configure fail2ban
sudo apt install -y fail2banSee Fail2ban Setup for configuration.
-
Limit SSH access to specific users
# /etc/ssh/sshd_config AllowUsers yourusername
Firewall (Critical)
A firewall blocks all traffic except what you explicitly allow. Without one, every service on your server is reachable from the internet.
-
Enable UFW and set default deny
sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow ssh # or your custom SSH port sudo ufw enableSee Firewall Setup with UFW for the full guide.
-
Only open ports you actually use — every open port is an attack surface
-
Review open ports regularly
sudo ss -tlnp
System Updates (Critical)
Unpatched software is the most common way servers get compromised after weak SSH.
-
Enable automatic security updates
sudo apt install -y unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades -
Update the system regularly
sudo apt update && sudo apt upgrade -y -
Update Docker images regularly — see Updating Docker Containers
-
Subscribe to security advisories for software you run (GitHub watch → releases)
Docker Security (High Priority)
Docker containers add their own security considerations.
-
Don’t run containers as root where possible
services: myapp: user: "1000:1000" -
Use read-only filesystems for containers that don’t need to write
services: myapp: read_only: true tmpfs: - /tmp -
Limit container capabilities
services: myapp: cap_drop: - ALL cap_add: - NET_BIND_SERVICE # Only add what's needed -
Don’t expose the Docker socket to containers unless absolutely necessary (like Traefik or Portainer). If you must, mount it read-only:
/var/run/docker.sock:/var/run/docker.sock:ro -
Pin image versions — never use
:latest -
Use non-root container images when available (many images support this)
-
Don’t use
--privilegedunless the app genuinely requires it (very few do) -
Review Docker network exposure — use internal networks for service-to-service communication
networks: internal: internal: true # Not accessible from host network
See Docker Security Best Practices for the complete guide.
Reverse Proxy Security (High Priority)
Your reverse proxy is the front door to all services.
- Force HTTPS everywhere — redirect all HTTP to HTTPS
- Use automatic SSL certificates from Let’s Encrypt — see Let’s Encrypt Explained
- Set security headers
Strict-Transport-Security: max-age=31536000; includeSubDomains X-Content-Type-Options: nosniff X-Frame-Options: DENY Referrer-Policy: strict-origin-when-cross-origin - Don’t expose admin interfaces (Portainer, database admin tools) to the internet — use SSH tunnels or VPN instead. See SSH Tunneling
- Rate-limit authentication endpoints
- Use basic auth or IP restriction for sensitive dashboards
Authentication (High Priority)
-
Use strong, unique passwords for every service — generate with:
openssl rand -base64 32 -
Enable two-factor authentication on every service that supports it — see Two-Factor Authentication
-
Use a self-hosted password manager like Vaultwarden to manage credentials
-
Change default credentials immediately after deploying any service
-
Don’t reuse passwords between services — if one is compromised, others remain safe
Network Security (Medium Priority)
- Disable UPnP on your router — prevents devices from opening ports without your knowledge
- Use VLANs to segment your network — see Subnets and VLANs
- Servers on one VLAN
- IoT devices on another (they can’t reach your server)
- Personal devices on a third
- Use Tailscale or WireGuard for remote access instead of port forwarding — see Tailscale Setup or WireGuard Setup
- Disable IPv6 if you’re not using it — it can bypass your IPv4 firewall rules
- Minimize port forwarding — prefer Cloudflare Tunnel for web services. See Cloudflare Tunnel
Data Protection (Medium Priority)
- Follow the 3-2-1 backup rule — see 3-2-1 Backup Rule
- Encrypt backups — especially off-site backups
- Test backup restores — a backup you can’t restore is not a backup
- Use full-disk encryption on your server (LUKS) if physical theft is a concern
- Store secrets in
.envfiles, not indocker-compose.yml— and ensure.envis in.gitignore - Never commit credentials to git
Monitoring and Detection (Medium Priority)
- Monitor system resources — see Monitoring Basics
- Set up uptime monitoring — use Uptime Kuma to alert on service failures
- Review logs regularly
# Check auth logs for suspicious activity sudo journalctl -u sshd --since "24 hours ago" # Check fail2ban bans sudo fail2ban-client status sshd - Monitor for unauthorized containers
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}" - Set up alerts for critical events — disk full, service down, unusual CPU/memory usage
DNS Security (Lower Priority)
- Use encrypted DNS (DoH or DoT) — see Encrypted DNS
- Run your own DNS resolver — Pi-hole or AdGuard Home
- Enable DNSSEC on your domain if your registrar supports it
Quick-Start: Minimum Viable Security
If you’re setting up a new server, do these 5 things first:
- SSH keys only — disable password auth
- Firewall on — UFW with default deny, allow only SSH + needed ports
- Automatic updates — unattended-upgrades enabled
- Fail2ban — installed and running
- Strong passwords — unique, generated, stored in a password manager
Everything else can be added incrementally.
Common Mistakes
Security Through Obscurity
Changing your SSH port to 2222 reduces log noise from bots, but it’s not real security. A determined attacker scans all ports. Focus on key-based auth and fail2ban first — those actually stop attacks.
Exposing Admin Interfaces
Portainer, phpMyAdmin, Adminer, Grafana — these should never be directly accessible from the internet. Use SSH tunnels, Tailscale, or IP-restricted reverse proxy rules. A compromised admin panel gives full control of your infrastructure.
Set and Forget
Security isn’t a one-time task. Software gets new vulnerabilities, containers need updates, configs drift. Review this checklist monthly. Check your firewall rules. Review what’s running. Update everything.
Oversharing in Error Pages
Default error pages from reverse proxies and web servers can leak software versions and internal paths. Configure custom error pages that don’t reveal server details.
Next Steps
- Start with SSH — SSH Setup
- Set up your firewall — Firewall Setup with UFW
- Harden Docker — Docker Security Best Practices
- Plan your backups — 3-2-1 Backup Rule
FAQ
How often should I update my server?
Enable unattended-upgrades for automatic security patches. For Docker images, check for updates weekly. For major OS upgrades, plan and test — don’t auto-upgrade between major versions.
Do I need a VPN if I use Cloudflare Tunnel?
Cloudflare Tunnel handles web services well. But for non-web access (SSH, database connections, internal tools), you still benefit from a VPN like Tailscale or WireGuard. They complement each other.
Is self-hosting less secure than cloud services?
It depends entirely on your configuration. A properly secured self-hosted server can be more secure than a cloud service — you control the data, the access, and the encryption. A poorly secured one is much worse. This checklist helps you get it right.
Should I use SELinux or AppArmor?
Both add mandatory access control that limits what processes can do. Ubuntu ships with AppArmor enabled by default. For most self-hosters, the default AppArmor profiles plus Docker’s built-in seccomp profiles provide good protection without additional configuration. Advanced users can create custom AppArmor profiles for high-risk services.
Related
Get self-hosting tips in your inbox
New guides, comparisons, and setup tutorials — delivered weekly. No spam.