State of the homelab
- Posted: August 15, 2026
- Updated: August 18, 2026
Currently, my homelab runs on one machine: a Beelink Mini PC with an AMD Ryzen 7 6800U (8C/16T), 19 GiB of RAM, and a 512 GB NVMe disk running Fedora 44. Everything is a systemd service on the host, except the Homepage dashboard, which runs in a rootless Podman container. The design goal was one: simplicity and reliability. I’ve had Kubernetes homelabs and fancy stuff in the past with Talos Linux, but never actualy depended on it.
┌────────────────────────────────────────────┐
│ CLIENTS │
│ (laptop or cellphone) │
└──────────────────────┬─────────────────────┘
│ *.yyvf.xyz → 100.72.197.102
▼ Tailscale VPN (encrypted, MagicDNS)
┌───────────────────────────────────────┴──────────────────────────────┐
│ HOST: beelink1 (Fedora 44) │
│ │
│ ┌──────────────────────┐ │
│ │ Caddy v2.11.4 │ wildcard *.yyvf.xyz (Cloudflare DNS-01) │
│ │ │ :80/:443 │
│ └──────────┬───────────┘ │
│ │ reverse_proxy (by hostname) │
│ ├──────────────▶ Forgejo :3000 git.yyvf.xyz │
│ ├──────────────▶ Homepage :3001 homepage.yyvf.xyz │
│ ├──────────────▶ Grafana :3002 grafana.yyvf.xyz │
│ └──────────────▶ gonic :4747 gonic.yyvf.xyz │
│ (127.0.0.1) │
│ │
│ Prometheus :9090 ──▶ node-exporter :9100 │
│ Tailscale :41641/UDP ──▶ 100.72.197.102 │
│ │
│ Container Podman rootless (user homepage): │
│ └── Homepage :3001 ▶ dashboard ghcr.io/gethomepage/homepage │
└──────────────────────────────────────────────────────────────────────┘
Tailnet as the network, DNS as the gate
There is no public exposure. All *.yyvf.xyz domains point at the host’s
Tailscale IP (100.72.197.102), so they only resolve for devices inside the
tailnet. Tailscale handles the encrypted tunnel, MagicDNS, and the client IPs —
no firewall rules open to the world, no reverse DNS leaks.
Services run the same domain names you’d use in production (git.yyvf.xyz,
gonic.yyvf.xyz, …), which means client configs and app URLs work identically
from anywhere on the tailnet.
Caddy is the only door
Caddy is the only process bound to the public interfaces, and the only one doing TLS. Backends stay on localhost or on ports the firewall doesn’t expose outside the tailnet.
The whole routing config is one site block:
*.yyvf.xyz {
tls { dns cloudflare "<token>"; resolvers 1.1.1.1 }
handle gonic.yyvf.xyz { reverse_proxy localhost:4747 }
handle git.yyvf.xyz { reverse_proxy localhost:3000 }
handle homepage.yyvf.xyz { reverse_proxy localhost:3001 }
handle grafana.yyvf.xyz { reverse_proxy localhost:3002 }
handle { respond "Not Found" 404 }
}
The wildcard certificate is issued with the Cloudflare DNS-01 solver, so
browsers trust it with no local CA to install. Adding a service is: bind it to a
localhost port, add one handle line, done.
The services
| Service | Port | URL | How it runs |
|---|---|---|---|
| Caddy | 80, 443, 2019 | — | systemd |
| Forgejo | 3000 | https://git.yyvf.xyz | systemd, sqlite, SSH on 22 |
| gonic | 4747 | https://gonic.yyvf.xyz | systemd, Subsonic API |
| Homepage | 3001 | https://homepage.yyvf.xyz | rootless Podman container |
| Grafana | 3002 | https://grafana.yyvf.xyz | systemd (Fedora package) |
| Prometheus | 9090 | http://localhost:9090 | systemd |
| node-exporter | 9100 | http://localhost:9100 | systemd |
| Tailscale | 41641/UDP | — | systemd |
Forgejo (a Gitea fork) stores git repos in sqlite under /var/lib/forgejo and
serves SSH on port 22 with SSH_DOMAIN = git.yyvf.xyz. gonic streams music over
the Subsonic protocol, so DSub/Subtracks work against gonic.yyvf.xyz from any
tailnet device. Grafana consumes the local Prometheus; Prometheus scrapes
node-exporter every 5s for host metrics.
Homepage runs as a rootless Podman container under a dedicated homepage user
with linger enabled, publishing port 3001. It has HOMEPAGE_ALLOWED_HOSTS
pointing at its hostname, so direct access via localhost:3001 is rejected on
purpose.
What’s maybe next
- Add scrape jobs of the services in Prometheus.
- Add forgejo-runner to run my actions in my self-hosted Forgejo and at Codeberg.