skovlund.dev
3 min read

miles

A self-hosted NixOS VPS running on Hetzner Cloud, fully managed from my nix-config repo. One make deploy-miles from my laptop rebuilds the entire system. Named after Miles Davis — I have a naming convention for my servers based on jazz legends, which is a very elaborate system for someone with exactly one server.

What runs on it

  • NetworkingTailscale mesh VPN keeps nearly everything off the public internet. The only public-facing service is a single Uptime Kuma status page behind Caddy.
  • ObservabilityPrometheus for metrics, Grafana for dashboards, Loki and Promtail for log aggregation. All Tailscale-only.
  • Uptime monitoring — Uptime Kuma tracks my own services and randersbigband.dk — I used to play in the band and still sub at rehearsals, so I offered to keep an eye on their site.
  • Notificationsntfy push notifications and Resend email alerts. I hear about everything: backups, disk usage, systemd failures, scrape health.
  • BackupsRestic snapshots to Backblaze B2, daily at 02:30 UTC. SQLite databases get .backup snapshots before archiving for consistency.
  • AI assistantEliza, an autonomous agent that manages my life — morning briefings, PR reviews, backlog, the works. All through Telegram.

Grafana Node Exporter dashboard showing CPU, memory, disk, and network metrics Node Exporter dashboard — CPU, memory, disk, and network at a glance.

Uptime Kuma status page with all services showing green All green. “When my sites are down I’m Kind of Blue.”

How it’s managed

Every piece of this infrastructure is declared in Nix modules under hosts/miles/ in nix-config. No manual server configuration, no imperative setup scripts, no drift. Secrets are agenix-encrypted at rest. The deployment flow: edit locally, make check, make deploy-miles over SSH.

Security

The short version: key-only SSH via Tailscale, fail2ban, kernel hardening, Hetzner Cloud Firewall, automatic NixOS upgrades, and Grafana alert rules for everything that might go wrong. Nothing is exposed to the public internet except a single status page. The long version: read the code.