Casa - My Home Lab
I run about 38 services on a single machine at home media streaming, home automation, ad blocking, password management, telegram bots, workflow automation - the works and managing all of that used to be a mess until I built casa
what is it
casa is a home lab management system built around Docker Compose
every service gets its own directory with a compose.yml and optionally a .env file
then there’s a central config.yaml that defines which services are active, their URLs, categories, and colors for the UI
the whole thing is a git repo, with secrets encrypted via git-crypt
the CLI
the core of casa is a CLI tool that wraps Docker Compose with some nice abstractions I went with Go for this because the compiled binary is tiny, it has zero runtime dependencies, and the code reads almost like pseudocode
casa up jellyfin # start a service
casa down sonarr # stop a service
casa status # see what's running
casa log homeassistant # tail logs
casa update jellyfin # pull + restart
casa install seerr # add a new service to config
casa reset transmission # wipe and start fresh
it reads config.yaml to know which services are active, parses each service’s compose.yml for metadata like URLs and ports, creates volume directories if they don’t exist, and restarts Caddy after config changes
nothing fancy - it just saves you from remembering which directory to cd into and which docker compose flags to pass
the services
here’s a taste of what’s running:
media
- Jellyfin - media server and streaming
- Sonarr - TV series management
- Radarr - movie management
- Lidarr - music management
- Bazarr - subtitle management
- Prowlarr - indexer management for the whole *arr suite
- Transmission - downloads
- Seerr - media discovery and requests, this is how the family finds what to watch next
- AudiobookShelf - audiobook and podcast server
- AudiobookBay - manages audiobook downloads for AudiobookShelf
infrastructure
- Caddy - reverse proxy with automatic HTTPS. I picked Caddy not just because it’s fast but because its config file is the simplest and most readable of any reverse proxy out there
- Pi-hole - network-wide DNS ad blocking. the real power here is the blocking lists - I use firebog which is a curated collection of lists covering ads, tracking, telemetry, and malicious domains
- Tailscale - mesh VPN for remote access. dead simple to set up but annoyingly it randomly invalidates auth tokens which makes long-running setups painful
- Portainer - Docker management UI
- Watchtower - automatic container image updates
smart home
- Home Assistant - running the house
- Friday - a Telegram bot I built for integrating with Home Assistant, so I can control the house from chat
tools
- n8n - workflow automation
- Vaultwarden - self-hosted password manager (Bitwarden compatible)
- Filebrowser - web-based file access
bots
- a bunch of Telegram bots I built for downloading, shopping lists, and AI chat
networking
every service gets a subdomain under home.shubapp.com - so Jellyfin lives at jellyfin.home.shubapp.com, Home Assistant at homeassistant.home.shubapp.com, and so on
the cool trick here (which I learned from Schniz) is that the domain just points to my server’s LAN IP so from outside my network it doesn’t resolve to anything - it’s basically a private domain that only works at home but you still get all the benefits of real DNS - proper subdomains, valid HTTPS certificates via Cloudflare DNS challenges, and no more typing IP addresses with port numbers
Caddy handles all the reverse proxying and TLS via Cloudflare DNS challenges
the Caddyfile is generated automatically from config.yaml - a TypeScript script reads each service’s compose file, extracts port mappings and labels, and builds the full config
services either share a Docker network called caddy (so Caddy can reach them by container name) or run with host networking when they need direct access to the network (like Home Assistant for device discovery)
Pi-hole runs as the network DNS so every device on the LAN gets ad blocking for free
the setup
the entire system bootstraps with a single Ansible playbook
./run-ansible.sh
it walks you through an interactive config - picks your static IP, username, domain, which services to enable - then it installs Docker, configures the network with netplan, mounts your external drives, creates all the volume directories, generates the Caddyfile, and starts everything
all configs and data live on external USB drives mounted at /media/external/
this way the OS drive can die and you just plug the drives into a new machine and run the playbook again
/media/external/
├── configs/ # service configs (jellyfin, sonarr, caddy, etc.)
└── library/ # media and data
├── tv/
├── movies/
├── music/
└── downloads/
adding a new service
adding a service is just creating a directory with a compose.yml:
services:
myapp:
image: some/image:latest
labels:
url: https://myapp.home.shubapp.com
title: My App
category: tools
color: "#4A90D9"
ports:
- "8080:8080"
networks:
- caddy
networks:
caddy:
external: true
then casa install myapp adds it to the config, regenerates the Caddyfile, and you’re live
why not kubernetes
I tried k8s at home, I really did
but for a single machine running personal services it’s just overkill
Docker Compose gives you everything you need - declarative config, networking, volume management - without the operational overhead
and when something breaks at 2am you want to debug a compose.yml, not a HelmRelease → Deployment → ReplicaSet → Pod chain
casa keeps the simplicity of compose but adds just enough tooling on top to make managing 38 services not feel like a chore