🏠 Self-hosted Linux infrastructure
Two Debian servers (Raspberry Pi 4) running 24/7. The goal: make services securely reachable from the internet without opening a single port on the router – and run them so that a failure gets noticed and can be fixed. This website runs on this infrastructure.
Setup
- Services: web server (nginx), password manager (Vaultwarden), DNS filter (AdGuard Home), file sharing, monitoring (Uptime Kuma, Netdata) – all as Docker containers, managed with Compose and Portainer.
- Access: published via Cloudflare Tunnel (Zero Trust) instead of port forwarding; remote administration via Tailscale with a subnet router into the home network.
- Second server: handles monitoring and receives the backups of the first server.
Security
- Key-only SSH, password and root login disabled; fail2ban against brute force.
- TLS ≥ 1.2, HSTS and security headers incl. Content Security Policy for the website.
- Public-facing container without root privileges, with a read-only file system and a process limit.
- Secrets (API tokens) kept separate from scripts in files readable only by the service user.
Backup & monitoring
- Bash script via cron: consistent database snapshot, archive of user data plus a system archive (configuration, crontabs, container inventory).
- The second server pulls the archives via read-only rsync access, verifies their integrity and rotates old versions.
- Notification on success or failure via messenger; Uptime Kuma raises an alarm if a backup does not run at all.
- Daily network inventory with nmap/ndiff – new devices or open ports are reported.
Lessons learned
A security review of my own infrastructure revealed, among other things, a publicly reachable service with too many privileges, backup files in the web root and a database in an ephemeral Docker volume. All fixed, documented and included in the backup concept – since then I deliberately verify changes from the outside.