Matthew Gisi

Docker Caddy Cloudflare Tunnel Headscale WireGuard Linode Self-Hosted Linux

Homelab —
Self-Hosted Stack

Personal infrastructure · Built & maintained independently

A Linux server running personal projects, family services, and dev tools — all in Docker Compose, routed through Caddy, exposed publicly via Cloudflare Tunnel with no open inbound ports, and reachable privately over a self-hosted WireGuard mesh coordinated by Headscale on a Linode VPS.

One server,
zero open ports.

🌐

Cloudflare Tunnel

The homelab initiates an outbound-only connection to Cloudflare. All public traffic flows through Cloudflare's network — no port forwarding, no exposed home IP, DDoS protection and HTTPS included out of the box.

↔️

Caddy Reverse Proxy

A single Caddyfile routes every domain and subdomain to the right container. All services share one external Docker proxy network — Caddy reaches them by container name without any manual port mapping.

🐳

Docker Compose Stacks

Each service or project is its own Compose stack. Shared infrastructure (PostgreSQL, Redis) lives in standalone stacks and is referenced by other containers over the internal Docker network.

🔒

Per-Service Isolation

Projects that handle sensitive data (Recall, Vaultwarden, Team Pakistan) run with isolated internal networks, their own databases, and are not reachable from other containers outside their stack.

🛰️

Headscale on Linode

A 1 GB Linode VPS (gisiremote) runs Headscale — an open-source Tailscale control plane. It coordinates a private WireGuard mesh between my devices and the homelab. The VPS only brokers handshakes; encrypted traffic flows peer-to-peer.

🔐

Two-Plane Networking

Public services ride Cloudflare Tunnel. Private admin surfaces (SSH, Portainer, code-server, internal dashboards) are reachable only over the tailnet at 100.64.0.0/10 — no public DNS, no exposed ports.

🛡️

Hardened VPS

Linode VPS runs UFW (22/80/443 tcp, 3478/41641 udp), key-only SSH with root disabled, fail2ban, and unattended-upgrades. Headscale auto-issues Let's Encrypt certs via HTTP-01.

A 5-dollar VPS
tied the homelab together.

The homelab sits behind a residential ISP — no static IP, no control over the upstream router. Cloudflare Tunnel solved the public-traffic side. The private side needed a different answer.

I provisioned the smallest Nanode on Linode (1 GB, Ubuntu LTS) and stood up Headscale with an embedded DERP server. The homelab joins the tailnet as one peer; phones, laptops, and remote workstations join through preauth keys. The VPS only brokers handshakes — encrypted traffic flows peer-to-peer.

Result: I can SSH to the homelab from anywhere, expose dashboards that never need to be public, and split traffic cleanly — Cloudflare for the world, the tailnet for me.

Control planeHeadscale v0.28.0
HostLinode Nanode (1 GB)
Hostnamegisiremote
Endpointprivate
DERPembedded
HardeningUFW · fail2ban · key-only SSH · unattended-upgrades

Services,
monitored.

Loading status…

60+
Containers running
0
Open inbound ports at home
1
Caddyfile
2
Network planes (CF Tunnel · Tailnet)
$5
VPS / month
100%
Self-hosted control plane