What actually runs on it

Everything here runs on two always-on machines at home in Denmark. No cluster, no orchestrator, no rack — just Docker Compose and a file of YAML I actually understand.

One machine holds the services: git, a password vault, the dashboard, and the tunnel that lets any of it be reached at all. The second exists to run things I do not trust, inside virtual machines it destroys afterwards.

Some of it is reachable from the internet. Most of it deliberately is not, and the difference is a decision made per service rather than a default.

Open ports
0
Machines
2
Certificates to renew
0
Monthly bill
electricity

Services

  • Private Git

    Private

    Self-hosted Forgejo. Repositories, issues and pull requests on hardware I own, rather than handed to someone else. Sign-ups are disabled and every page requires authentication — it exists for exactly one user, and it is not linked from here.

    • Forgejo
    • SQLite
    • Docker
    Not published
  • Password vault

    Public

    A Bitwarden-compatible vault, reachable only through the tunnel. It also holds the encrypted bundle of every other secret in here, which turns rebuilding the whole setup into a restore rather than an excavation.

    • Vaultwarden
    • Docker
    Not published
  • Task worker

    Public

    Hands a job to a throwaway virtual machine, lets it work, then destroys the machine. Link and mail analysis, dev chores, container builds. The sandbox gets filtered egress and no route back inward, because the whole point is running things I would not run on a real host.

    • Incus
    • FastAPI
    • Cloudflare Access
    Gated at the edge
  • Dashboard

    Local only

    Container health, host resources, backup history and tunnel state on one pane of glass. Kept off the internet on purpose: it reads the Docker API, and nothing that can enumerate containers belongs on a public hostname.

    • Homepage
    • docker-socket-proxy
    • Dozzle
    Local network only
  • Cloudflare Tunnel

    Public

    How anything gets in at all. The server dials out and holds the connection open, so there is no port forwarding, no static IP, and nothing on the router listening for strangers.

    • cloudflared
    • QUIC
  • Backups

    Private

    Both machines snapshot themselves nightly and push offsite, encrypted before anything leaves the house. Verified by actually restoring and cloning from the result — an untested backup is a rumour, not a backup.

    • restic
    • rclone
    • systemd timers
    Nightly, offsite
  • Pep-talk

    Private

    A Fantasy Premier League helper. Each week it works out what to do with the team — transfers, captain, bench — from the numbers rather than a hunch. Built for one manager, hosted here, not published.

    • Fantasy Premier League
    • Docker
    Not published
  • This site

    Public

    The page you are reading. Static files served by nginx on the server, rebuilt straight from the git repository a few minutes after anything lands on the default branch. It reaches you the same way everything else here does — down the tunnel. No third-party host, no build service.

    • Astro
    • nginx
    • Docker

No open ports, anywhere

The usual way to self-host is to forward a port on the router and hope. This does the opposite: the server dials out to Cloudflare and keeps that connection open. Traffic arrives down a pipe the server itself opened, so there is nothing inbound to find, scan or brute-force.

Home lab architectureTwo always-on machines behind a home router. A browser reaches Cloudflare's edge, which passes traffic down a tunnel the server itself opened outward. The site itself is one of the containers behind that tunnel. Both machines also push encrypted backups outward to offsite storage. No connection is ever made inward through the router.THE INTERNETMY NETWORKROUTER · NOTHING LISTENINGA browseranywhere in the worldCloudflare edgeterminates TLScertificates handled hereOffsite storageencrypted beforeit leaves the househttpsthe serverDebian 13 · Docker Compose · 8 corescloudflaredholds the tunnelwwwserves this siteForgejoprivate gitVaultwardenthe vaultTask APIqueues the workDashboardlocal network onlythe workerArch · Incus · runs what I do not trustagentpulls jobsone at a timesandbox VMfiltered egressno way back inthen destroyedrebuilt from aclean snapshothands over a job
The tunnel — dialled outward by the server, then held open.Backups — nightly from both machines, encrypted before they leave.
  1. 01

    Your browser

    Asks for a hostname on notacloudserver.net.

  2. 02

    Cloudflare edge

    Terminates TLS. Certificates are its problem, not mine.

  3. 03

    Encrypted tunnel

    A QUIC connection the server opened, outbound.

  4. 04

    cloudflared

    A container on the server holding the other end.

  5. 05

    The service

    Answers on the Docker network. Never on the router.

The whole thing, listed

Hosts
Two x86 machines, always on
Service OS
Debian 13 (trixie)
Worker OS
Arch, Wayland, tiling
Runtime
Docker Engine + Compose
Sandboxing
Incus containers and VMs, thrown away after use
Ingress
Cloudflare Tunnel, outbound only
Edge auth
Cloudflare Access, one-time PIN
Version control
Self-hosted Forgejo
Dashboard
Homepage, with per-container logs
Backups
restic to object storage, nightly
Secrets
age-encrypted bundle, held in the vault
This site
Astro, rebuilt on a timer, served by nginx
Open ports
Zero
Start

Under construction

Get in touch

Contact

Calling…

Happy to talk about homelabs, packaging, automation, or anything else on this site.

Email
Needs JavaScript — the address is not written into the page.Hidden from scrapers until you ask for it.
Location
Denmark

Replies are faster on weekday evenings than at any other time.