Self-Hosting Best Practices
A practical reference for securing, maintaining, and operating self-hosted services.
1. Security Foundations
SSH Hardening
SSH is the primary attack vector for automated bots.
- Disable password authentication β use key-based auth only.
bash # /etc/ssh/sshd_config PasswordAuthentication no PubkeyAuthentication yes - Disable root login over SSH.
bash PermitRootLogin no - Use Ed25519 keys (not RSA).
bash ssh-keygen -t ed25519 -C "your-email@example.com" - Change the default port (optional but reduces log noise).
- Install fail2ban to rate-limit brute-force attempts.
- Limit SSH access to specific users via
AllowUsers. - Additional hardening:
bash # /etc/ssh/sshd_config ClientAliveInterval 300 ClientAliveCountMax 2 MaxAuthTries 3
Firewall
Enable a host firewall with a default-deny inbound policy.
# UFW (Debian/Ubuntu)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw enable
Only open ports you actually use. Review open ports regularly:
sudo ss -tlnp
System Updates
Unpatched software is the leading cause of compromise.
- Enable automatic security updates:
bash sudo apt install -y unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades - Update the system regularly:
bash sudo apt update && sudo apt upgrade -y - Subscribe to security advisories for self-hosted applications.
2. Network Security
- Disable UPnP on your router β prevents devices from opening ports without your knowledge.
- Use VLANs to segment your network (servers, IoT, personal devices).
- Use Tailscale or WireGuard for remote access instead of port forwarding.
- Tailscale is recommended for most users β WireGuard encryption with automatic key management and NAT traversal.
- Self-hosted WireGuard or Headscale if you need zero third-party dependency.
- Disable IPv6 if unused β it can bypass IPv4 firewall rules.
- Minimize port forwarding. Prefer Cloudflare Tunnel for web services.
3. Remote Access β Tailscale
Tailscale creates a secure mesh VPN using WireGuard underneath.
Installation:
curl -fsSL https://tailscale.com/install.sh | sudo sh
sudo tailscale up
Benefits: - No port forwarding needed - Devices reach each other by name - NAT traversal works behind most routers/CGNAT - Access controls via ACLs (Tailscale admin console)
Use cases: - SSH into your server from anywhere without exposing port 22 - Access admin interfaces privately (no need to expose to internet) - Treat remote devices as if theyβre on the same LAN
4. Reverse Proxy & TLS
- Force HTTPS everywhere β redirect all HTTP to HTTPS.
- Use Letβs Encrypt / ACME for automatic certificates.
- Set security headers:
text Strict-Transport-Security: max-age=63072000; includeSubDomains; preload X-Content-Type-Options: nosniff X-Frame-Options: DENY Referrer-Policy: strict-origin-when-cross-origin - Restrict TLS protocols to 1.2/1.3.
- Monitor certificate expiry and set alerts for < 14 days.
5. Authentication & Access Control
- Use strong, unique passwords for every service.
- Enable two-factor authentication on every supported service.
- Use a self-hosted password manager like Vaultwarden.
- Change default credentials immediately after deploying any service.
- Apply least privilege:
- Separate admin users from service users.
- Avoid running services as root.
- Limit API tokens to only required scopes.
- Donβt expose admin interfaces (Portainer, database tools) to the internet. Use SSH tunnels or VPN instead.
6. Secrets Management
- Store secrets in
.envfiles, not indocker-compose.ymlor git. - Encrypt sensitive configuration with tools like
sops+age. - Never commit credentials to git β even in private repos.
- Automate rotation where supported (database passwords, API tokens).
- Store backup passwords separately from backup data.
7. Logging & Monitoring
What to Log
- SSH logins (success and failure)
- sudo invocations
- Firewall drop events
- Application errors and access logs
- Systemd service state changes
Monitoring Stack
- Uptime monitoring: Uptime Kuma, Healthchecks.io
- System metrics: Netdata, Grafana + Prometheus
- Logs: Ship logs off-host; use Grafana Loki or a remote syslog target
Alerts to Configure
| Event | Signal | Threshold |
|---|---|---|
| SSH brute force | Failed logins | > 10 in 5 min |
| New SSH source IP | Success from new address | Any |
| Service down | systemd active state | failed |
| Disk full | filesystem usage | > 85% |
| TLS cert expiry | days_until_expiry | < 14 days |
| Unexpected sudo | sudo log line | Any non-whitelisted command |
Monthly Review
- Check auth logs
- Run system updates
- Review fail2ban bans
- Scan open ports
- Test a backup restore
8. Backup Strategy (3-2-1 Rule)
- 3 copies β live data + 2 backups
- 2 different media β e.g., SSD + HDD, local + cloud
- 1 offsite β physically separate from the server
What to Back Up
| Data Type | Priority | Frequency |
|---|---|---|
| Application databases | Critical | Every 4-6 hours |
| User-generated content | Critical | Daily |
| Configuration files | High | Daily or on change |
| Docker volumes | High | Daily |
| Media libraries | Medium | Weekly |
| Logs | Low | Optional |
Tools
- Restic β fast, always encrypted, works with every backend (local, S3, Backblaze B2, etc.)
- BorgBackup β best dedup ratio for large datasets
- Kopia β CLI + GUI option
- Duplicati β beginner-friendly web UI
Automation
Prefer systemd timers over cron for better logging and dependency handling.
Test Restores Monthly
A backup you havenβt tested is not a backup.
restic -r /path/to/repo snapshots
restic -r /path/to/repo restore latest --target /tmp/restore-test
Monitor Backups
- Heartbeat monitoring (Uptime Kuma, Healthchecks.io)
- Alert if newest snapshot is older than 48 hours
- Check backup volume free space
9. Maintenance Schedule
| Frequency | Task |
|---|---|
| Daily | Automated security updates, backups run |
| Weekly | apt update && apt upgrade -y, review auth logs |
| Monthly | Fail2ban stats, port scan, Lynis audit, backup restore test |
| Quarterly | Service inventory review, update runbooks, DR test |
| Annually | Major OS upgrades, hardware health checks |
10. Incident Response
- Keep an inventory of services, versions, and patch dates.
- Document recovery procedures for each service.
- Know how to restore from backup before you need it.
- Have a rollback plan for updates.
- Run a firewall:
ufw/nftableswith default deny. - Block brute force:
fail2banon SSH and web services. - Patch automatically:
unattended-upgradesfor security fixes. - Use key-only SSH, disable root, rate-limit ports.
- Back up offsite, encrypted, with tested restores.
11. Recommended Tooling
| Category | Tools |
|---|---|
| Firewall | UFW, nftables |
| SSH hardening | fail2ban, sshd_config tuning |
| Reverse proxy | Nginx, Caddy, Traefik |
| SSL | Certbot, acme.sh, Caddy (auto) |
| Monitoring | Netdata, Grafana + Prometheus, Uptime Kuma |
| Backups | Restic, BorgBackup, Duplicati |
| Security scanning | Lynis, Trivy (container images) |
| Logging | Grafana Loki, rsyslog β remote target |
| Remote access | Tailscale, WireGuard, Cloudflare Tunnel |
| Password manager | Vaultwarden |
| Intrusion detection | AIDE, auditd, Wazuh (optional) |
Sources
- Self-Hosting Security Checklist β selfhosting.sh
- Self-Hosted Backup Strategy Guide β selfhosting.sh
- Self-Hosting Security Reference β slicker.me
- Securing Your Home Server β selfhostsetup.com
- Is Self-Hosted Server at Home Safe? β uk-servers.co.uk
- Home Server Mastery β hsecurities.in