Services: What Runs Where and How It’s Exposed
Concrete list of what I run, which host/VM it’s on, and how nginx and TLS are configured.
Reverse proxy (nginx) — HA pair
- Keepalived MASTER (Proxy Host): Bare-metal Ubuntu in the network closet. Holds the VIP under normal operation. Runs nginx, keepalived, certbot, fail2ban, UFW. DDNS updater starts via keepalived notify when this node holds the VIP.
- Keepalived BACKUP / config primary (nginx VM on Desktop Host): Ubuntu VM. Assumes VIP when the Proxy Host fails. Runs nginx, certbot, keepalived, inotify-based config sync to the Proxy Host, and DDNS when it holds the VIP.
- Config: Homelab vhosts live in per-service files under
conf.d/(e.g.homepage.conf,vaultwarden.conf,plex.conf,uptime-kuma.conf). Mainnginx.confhandles process/events/http, TLS defaults (1.2/1.3, modern ciphers), andinclude conf.d/*.conf. - Sync: Config and cert sync from config primary (nginx VM) to the Proxy Host is active (inotify-based). Either node can hold the VIP and serve all traffic.
nginx documentation · keepalived
DNS — Pi-hole × 3 (synced)
- Instance 1: Docker container on the Docker VM on the Desktop Host (
network_mode: host; web admin proxied at e.g.pihole01.detellem.com, LAN-only via nginxallow/deny). - Instance 2: Docker container on the NAS Host (same IP as the NAS, different port). Proxied at e.g.
pihole02.detellem.com, LAN-only. - Instance 3: Docker container on the Proxy Host (
network_mode: host). Proxied at e.g.pihole03.detellem.com, LAN-only.
Three Pi-holes plus one Google DNS fallback are in the DHCP “DNS servers” list. Nebula Sync replicates config from the primary to both replicas every 15 minutes. Pi-hole admin is not exposed to the internet; nginx returns 403 for those LAN-only hostnames.
Main landing — Homepage
- Where: Docker container on the Docker VM (Desktop Host).
- Role: Dashboard with links to all my services (Plex, Vaultwarden, Mealie, etc.). Served at the root domain and at
www,home,dashboard,homepagesubdomains (one nginx server block, same backend). - Config: YAML for widgets and services; I version the config in the homelab repo and mount it into the container.
Plex
- Where: Desktop Host (Windows), not in a VM — same machine as Hyper-V and Ollama.
- How it’s exposed:
plex.detellem.comthrough nginx (TLS at the proxy) to the Plex port on the Desktop Host. - Media: Libraries on the NAS Host over SMB; RTX 3070 for hardware transcoding.
Vaultwarden (password manager)
- Where: Single Docker container on the Docker VM (Desktop Host). Vaultwarden is a lightweight Rust reimplementation of the Bitwarden server API (~50MB RAM, SQLite database).
- Exposed as:
bitwarden.detellem.com. Client max body size increased in nginx for attachments. Works with all official Bitwarden clients (iOS, Android, browser extensions, desktop, CLI). - Data: SQLite database and file attachments; automated nightly backups to NAS Host, encrypted at rest with GPG (AES-256 symmetric).
Vaultwarden · Vaultwarden Wiki
Paperless-ngx (document management)
- Where: Docker on the Docker VM (Desktop Host). Five containers: main application (web UI + consumer + celery workers), Redis (task queue), PostgreSQL (metadata), Gotenberg (office → PDF), and Tika (content extraction).
- Exposed as:
paperless.detellem.com. OCR, full-text search, automatic tagging, and correspondent detection. - Storage: Document media (originals + archive) on the NAS Host via NFS mount. PostgreSQL data in a local Docker volume. Consumption folder auto-imports any file placed in it.
Mealie (recipes)
- Where: Single Docker container on the Docker VM. One port mapped on the host.
- Exposed as:
recipes.detellem.comandmealie.detellem.com(same server block). - Data: Docker volume; backed up nightly to NAS Host.
Donetick (chores)
- Where: Docker container on the Docker VM.
- Exposed as:
chores.detellem.comanddonetick.detellem.com.
IT-Tools
- Where: Docker container on the Docker VM. Stateless.
- Exposed as:
it-tools.detellem.com.
ConvertX (file conversion)
- Where: Docker container on the Docker VM. Needs a larger
client_max_body_sizein nginx for uploads (e.g. 1G). - Exposed as:
xconvert.detellem.comandconvertx.detellem.com.
OpenWebUI (LLM chat)
- Where: Docker container on the Docker VM (Desktop Host). Backend: Ollama running on the Desktop Host Windows machine using the NVIDIA RTX 3070.
- Exposed as:
ai.detellem.com,llm.detellem.com. Built-in auth (email/password); WebSocket and streaming-safe nginx config. (chat.detellem.comwas repurposed for Stoat Chat — see below.)
Stoat Chat (self-hosted Discord alternative)
- Where: Dedicated Ubuntu VM on the Desktop Host (Hyper-V). 15 containers (API, web frontend, voice via LiveKit, MongoDB, file storage via MinIO, message broker via RabbitMQ, etc.).
- Exposed as:
stoat.detellem.comandchat.detellem.com. nginx reverse proxy for HTTP/WebSocket; nginx stream proxy for LiveKit voice/video (TCP + UDP). Invite-only registration; CincySMP-branded frontend (custom CSS theme, wordmark, and favicon injected via volume mounts). - Backups: Automated daily (MongoDB dump, MinIO data, config) to NAS Host; 7 daily + 4 weekly retention.
Minecraft (game server + landing page)
- Where: Dedicated Ubuntu VM on the Desktop Host (Hyper-V). Not a Docker container — runs as a native systemd service with its own 6 vCPUs and 32GB RAM.
- Exposed as:
cincysmp.detellem.comon port<MINECRAFT_PORT>(raw TCP, not HTTPS). Nginx’sstreammodule on the Proxy Host forwards the connection and passes the real client IP via PROXY protocol. - Landing page: A separate Docker container on the Docker VM serves
cincysmp.detellem.comover HTTPS — an Astro static site with server status, resource pack download, and server info. nginx routes HTTP traffic to the landing page and TCP game traffic to the Minecraft VM. - Security: Mojang authentication (online-mode), whitelist-only, fail2ban connection-flood jail on the proxy, UFW on the VM allowing only the proxy IP.
Uptime Kuma (monitoring)
- Where: Docker container on the Docker VM (Desktop Host).
- Exposed as:
status.detellem.comanduptime.detellem.com(LAN-only via nginxallow/deny). - Role: HTTP/HTTPS uptime checks, status dashboard, and alerting for service downtime.
NAS Host
- Where: Dedicated machine. Ubuntu + ZFS (RAIDZ1 on 4×8 TB). Not exposed through the reverse proxy to the internet. SMB shares are used by the Desktop Host and other devices. Datasets hold media, backups, and container data. Pi-hole 2 runs as a Docker container on the same host.
- Role: Central storage and backup target for the entire homelab.
Summary table
| Service | Host/VM | Exposed as | Notes |
|---|---|---|---|
| Homepage | Docker VM (Desktop) | detellem.com, www, home… | Main dashboard |
| Plex | Desktop Host (Win) | plex.detellem.com | Media on NAS (SMB); GPU transcode |
| Vaultwarden | Docker VM (Desktop) | bitwarden.detellem.com | Single container; GPG backups |
| Paperless-ngx | Docker VM (Desktop) | paperless.detellem.com | 5 containers; NFS storage on NAS |
| Mealie | Docker VM (Desktop) | recipes / mealie | Single container |
| Donetick | Docker VM (Desktop) | chores / donetick | Single container |
| IT-Tools | Docker VM (Desktop) | it-tools.detellem.com | Stateless |
| ConvertX | Docker VM (Desktop) | xconvert / convertx | Large uploads |
| OpenWebUI | Docker VM (Desktop) | ai / llm.detellem.com | LLM chat; Ollama on Desktop GPU |
| Uptime Kuma | Docker VM (Desktop) | status / uptime.detellem.com | LAN-only monitoring |
| Stoat Chat | Dedicated VM (Desktop) | stoat / chat.detellem.com | Self-hosted Discord alt; voice via LiveKit |
| Minecraft | Dedicated VM (Desktop) | cincysmp.detellem.com:<MINECRAFT_PORT> |
Paper; TCP via nginx stream |
| CincySMP Landing | Docker VM (Desktop) | cincysmp.detellem.com (HTTPS) | Astro static site + status API |
| Pi-hole 1–3 | Docker VM, NAS Host, Proxy Host | pihole01/02/03.detellem.com | LAN-only; 3/3 active; Nebula Sync |
| nginx HA | Proxy Host (MASTER); nginx VM (BACKUP) | — | VIP, TLS; HA pair |
| Grafana | Docker VM (Desktop) | logs.detellem.com | LAN-only; dashboards + alerting |