The problem
I've been running my own servers for about five years. It started with game servers for friends, then a single Ubuntu box serving PHP sites straight out of /var/www. Every new project added one more thing to that box, until "the server" was a list of things that would all break together.
What I wanted:
- a place to run everything I build and use, from production web apps to GPU workloads
- clear boundaries, so one project's mistake stays inside that project
- enough structure that adding the next site is routine instead of an evening of hand-editing configs
This page is the platform as it stands today. The evolution timeline covers how it got here.
Architecture
The host
One physical server runs XCP-ng:
- a 2014-era Intel Xeon E5-2620 v3
- 128 GB of DDR4
- three GPUs passed through to a single VM: an RTX 3060 Ti for AI workloads (ComfyUI, Open WebUI) and two Tesla M60s that handle video encoding
Xen Orchestra runs in its own VM to manage the host. I have other machines, but this is the one that matters.
The VMs
Each VM has one job:
| VM | Job |
|---|---|
| Proxy VM | The edge. nginx terminates TLS and routes every subdomain to the right backend |
| dylanbanta.com VM | Docker Compose stacks for dylanbanta.com projects |
| sigmusdewn.com VM | Docker Compose stacks for sigmusdewn.com projects |
| GPU VM | Everything that needs a GPU, whichever domain it belongs to |
| NAS VM | Samba shares for Windows machines, NFS exports for Linux machines and the other VMs |
| XOA VM | Xen Orchestra, the management plane for the hypervisor |
Splitting the app hosts by domain means a bad deploy or a runaway container on one side doesn't take down the other. Putting every GPU workload on one VM means the passthrough only has to be configured once.
The edge
All public traffic goes through Cloudflare to one nginx VM. It holds:
- ~130 virtual hosts across four domains (sigmusdewn.com, dylanbanta.com, binaryprosperity.com, and an older dylanbanta.com set I'm retiring)
- 91 Let's Encrypt certificates, issued and renewed by certbot
- TCP stream proxying for non-HTTP services, such as git over SSH and game servers
- a WebSocket upgrade map for services that keep long-lived connections, like push notifications
- a few vhosts behind basic auth, and a proxy cache
On September 29, 2026 I removed TLS 1.0 and 1.1 globally and checked every vhost afterwards. The edge now accepts only TLS 1.2 and 1.3.
One detail I like: the proxy VM's /etc/nginx is a symlink to a folder on the NAS, mounted over NFS. That folder is the live configuration, not a backup. Every app host and my workstation can read the same config, and the provisioning tools I built write vhosts there directly.
Storage
The NAS is a Kubuntu VM sitting on the host's hardware RAID array (about 6 TB usable with parity). It exposes three 2 TB areas:
- the Luterra project data
- a public share
- the server data that the app VMs mount
A separate 1 TB disk is shared as a network-wide Users folder, so my Documents and other folders stay in sync across Windows and Linux machines.
Networking
I'll keep this general on purpose. The network uses VLANs for segmentation, WireGuard for remote access, and Cloudflare Tunnel (cloudflared) where a service shouldn't be reachable any other way.
Sample configuration
A typical proxied vhost. Real names and ports are replaced with placeholders, but the shape matches production:
server {
server_name app.example.com;
location / {
proxy_pass http://app-host:10xxx/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
}
location /admin/ {
# Admin tools are LAN-only: allow the LAN range, deny all
include snippets/lan-only.conf;
proxy_pass http://app-host:10yyy/;
}
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
}
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}
The global TLS policy after the September cleanup:
# http {} block
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
Raw TCP services go through the stream module instead of an HTTP vhost:
stream {
# git over SSH to the Gitea host
server {
listen 222;
proxy_pass git-host:22xx;
}
# a game server
server {
listen 25565;
proxy_pass game-host:25xxx;
}
}
Key decisions
XCP-ng over the alternatives
I wanted a type-1 hypervisor that I could manage from a web UI and script against, without a license wall in front of features I'd actually use. XCP-ng with Xen Orchestra gave me that, and it has been dependable for years.
A self-built NAS instead of TrueNAS
I ran TrueNAS (both CORE and SCALE) for a while, then replaced it with a plain Kubuntu VM running Samba and NFS. TrueNAS is good software, but I didn't like how much it hid from me or its appliance-style limits. I wanted to own the configuration and know exactly what every share was doing.
If I built it again, I'd use Ubuntu Server without a desktop environment. The desktop doesn't cost much, but it's nothing the NAS needs.
One proxy VM in front of everything
Every public request goes through the same place, so:
- TLS policy is enforced in one spot (the TLS 1.0/1.1 removal was one change, then a check of every vhost)
- certificates are managed in one spot
- I can see every route the platform exposes in one directory
Each project in its own Compose stack
Every project gets its own Docker Compose stack with its own network, bind-mounted volumes and its own database. A broken migration or a compromised app stays inside its stack. This is also what made automated provisioning possible later: once every site has the same shape, a template can generate the next one. See dlt and dev-llc.
My own toolchain
I run my own ops toolchain:
- two Gitea instances for source control
- Dozzle for container logs, behind a docker-socket-proxy so the log viewer never gets raw Docker socket access
- ntfy for push alerts
- osTicket for tracking requests
- Vaultwarden for secrets, with update and backup scripts
- Dashy and Portainer for at-a-glance views
What broke and how I fixed it
- Old TLS versions were still enabled. An audit for this portfolio found TLS 1.0 and 1.1 still turned on globally. I disabled them, then checked every vhost, because a vhost can override the global setting.
- Lots of small outages. Running about 90 services for five years means fixing a steady stream of small problems: containers that don't come back after a reboot, certificates that fail to renew, a mount that wasn't ready when a stack started. None of them make a good story on their own. Together they're most of what I know about operations.
Also running
Not everything here is something I built. Plenty of it is good existing software, run well. Those are listed in the self-hosted apps section below.
Self-hosted apps
Productivity & ops
I build from scratch a lot, but I also know when an existing tool is the right call.
- n8n: workflow automation, with pgvector and Redis
- Gitea: two instances, one per domain
- Vaultwarden: password vault, with update and backup scripts
- Dozzle: container logs, behind a docker-socket-proxy
- ntfy: push notifications for my own services
- osTicket: helpdesk, wired to ntfy
- Portainer: container management
- Dashy: homelab dashboard
- Excalidraw: self-hosted, plus a fork with a storage backend
- Kanboard: project boards
- Nextcloud: file sync
- Cal.com: consultation booking
- Umami: privacy-friendly analytics
- Actual: budgeting
- Habitica: habit tracking
- VERT: file conversion in the browser
- Webtop: a desktop in the browser
- Guacamole: browser-based remote desktop
- Remotely: remote support
- Jupyter: notebooks
Media & books
- AzuraCast: internet radio
- Navidrome: music streaming
- MusicBrainz Picard: music tagging
- Download pipeline: automated music downloads that feed my dj app
- Calibre: ebook library
- Kavita: reading server
- Audiobookshelf: audiobooks
Game servers
Years of operating multi-user services for real players: mods, backups, CVE patching and TCP proxying.
- Minecraft: my own modpack, run for years
- FiveM / QBCore: GTA V roleplay server
- SCP: Secret Laboratory: dedicated server
- Foundry VTT: tabletop RPG server
History
- WordPress: several sites, 2022–24, before I moved to Laravel