Internet
Two egress paths reach it, and they are not interchangeable. Ordinary services leave through the LAN gateway. Isolated workloads can only reach it through the VPN provider, because the firewall inside their namespace drops everything else.
Infrastructure
One Proxmox node, held to production standards. Backups get restored on a stopwatch, a watchdog notices when anything goes quiet, probes repair what they safely can, and the workloads that must never leak fail closed. The useful part is what broke along the way, and what I changed.
This is what runs on it: 7 guests (3 VMs, 4 LXC containers) carrying 31 Docker containers. Atlas, my private voice for alerts, and Jarvis, a public bot, live here, one trusted and one deliberately not, and so does my Mealie fork. One GPU does four jobs. The security controls in front of all of it have their own page.
In one line: everything reaches the internet through one hardened front door, and the apps that must stay private are blocked from every other way out. Run the scenarios below to watch it, or click any box for what it runs and why.
Pick a scenario and watch the packet move.
Click a box, or focus it and press Enter.
Select a component, or trace a path, to see how egress is controlled.
Two egress paths reach it, and they are not interchangeable. Ordinary services leave through the LAN gateway. Isolated workloads can only reach it through the VPN provider, because the firewall inside their namespace drops everything else.
The single internet egress for the isolated namespace. Anything else headed for the internet is dropped before it leaves the namespace, so this is not the preferred path out. It is the only one allowed.
The normal route to the internet for ordinary services. The isolated workloads cannot use it to get out: their namespace's firewall drops by default and accepts only the tunnel, the VPN handshake, and the local subnet the media stack talks to.
One host, carved into VMs and LXC containers. Every guest carries its own default-deny firewall rather than relying on the cluster firewall, so the host is not the only thing between a service and the network, and guests stay portable.
Runs most of the containerized apps. The ones that must only ever use the VPN share one network namespace with the VPN container instead of having a network of their own.
Everything the isolated workloads send to the internet leaves through here. If it drops, they do not fall back to the home connection, they lose the internet until it comes back, which is the correct failure mode. I killed it six times to check: 45 probes during the outages, none answered, and it recovered on its own in 23 to 61 seconds.
Containers that need internet access but must never use the home connection. They share the VPN container's network namespace, where the firewall's default is drop. A leak would need a rule that is not there, and I check that by killing the tunnel, not by reading the config.
All TLS terminates here and every service sits behind it. Nothing is exposed directly, so there is one front door to harden instead of a dozen.
The user-facing intake, kept separate from the workloads that act on what it receives. A blast-radius decision: the part strangers touch holds the API keys it needs to pass a request on, but has no media library and none of the apps that act on requests.
The RTX 3070 is passed through to this VM alone and does four jobs: NVENC transcoding for playback, speech for the voice stack, a local language model for translation, and image upscaling. Transcoding wins: the upscaler pauses whenever a stream holds an NVENC session, and waits for the card to cool from 75 °C to 65 °C before it continues.
Hardware transcoding on the 3070. This is the job the GPU was bought for. Everything else on the card is batch work that gets out of its way.
Whisper for speech-to-text and Kokoro for the voice, a local model for translation, and an upscaler. All of it runs on the same card, with no cloud API in the loop. Measuring the speech pipeline is what showed that text-to-speech, the stage that sounds slow, is the fastest part of a response.
Home Assistant runs the house, in its own container. Atlas is the voice on top: a watcher on the hypervisor reads health, security and self-heal state every 60 seconds, and Home Assistant decides what gets said and where, on my PC when I'm at it, otherwise my phone. Only I hear it, which is why it is allowed to know all of this.
The Discord bot my friends talk to. It runs on the container host with 18 tools (music, polls, timers, web search, media requests), and none of them touch the house: there is no Home Assistant credential in its environment and no Home Assistant code in its image.
A fork of the recipe manager that I build and deploy here, with an acceptance check on every change. Running what I build is the point: every regression I ship lands on the machine I use at dinner.
Six pages that lead with a one-line verdict: health, security, media, books, a monthly tool radar, news. Acknowledgements happen on the page with an undo, and actions are signed links. My phone reads a summary of it over a private tunnel.
Netdata keeps per-second metrics. On top of it, a watchdog checks that 168 jobs and feeds are still updating, and 72 health probes repair what they safely can. Both are explained below. An AI assistant can read all of it through read-only tokens; any change it proposes waits for my go.
Guest disks live on a two-disk mirror, the media library on a second mirror. Nightly backups go to a separate disk and a weekly copy goes to the media mirror, so one dead disk never takes a guest and its only backup together.
In one line: if anything stops updating, I hear about it before I would ever notice it myself. Monitoring usually answers "is it up?". The failure I care about more is quieter: a scan that stopped running, a backup that stopped landing, a dashboard that shows last Tuesday as if it were today. Every 10 minutes a watchdog finds every scheduled job on all eight hosts and every data feed the dashboard reads, by itself, and checks each one is still updating. Right now that is 168 checks, 168 of them fresh.
clock +0 h 00 min
| Job or feed | Runs | Stale after | Age | State |
|---|---|---|---|---|
| Dashboard health feed | every 10 min | 30 min | 0 min | fresh |
| Self-heal status | every 10 min | 25 min | 0 min | fresh |
| Container vulnerability scan | daily | 26 h | 0 min | fresh |
| Hypervisor config backup | daily | 26 h 30 min | 0 min | fresh |
| Backup integrity check | weekly | 8 days | 0 min | fresh |
In one line: every guest is backed up twice, to different disks, and restores are drilled on a stopwatch. A backup that has never been restored is a hope.
| What | When | Where | How |
|---|---|---|---|
| Every running guest | Daily 04:00 | Separate backup disk | Snapshot mode, zstd |
| Every running guest, second copy | Sunday 05:30 | Media mirror (different disks) | Snapshot mode, zstd |
| Hypervisor config and scripts | Daily 03:17 | Backup disk | 7 rolling + monthly |
| Archive integrity check | Sunday 08:41 | Both targets | Newest archive per guest, alert only on problems |
| Backup age | Every 10 min | Watchdog | Alert if any guest's newest backup is over 8 days old |
restore drill · 2026-10-22
Restored with its network unplugged; every SIEM component came up.
Timed from the hypervisor task log 19:20:46 to 19:21:25.
restore drill · 2026-10-30
Database integrity ok; users, requests and media rows identical to live (7 / 226 / 611).
Timed from the hypervisor task log 14:15:15 to 14:16:50.
Both drills restore into a throwaway copy with no network, so a restored service can never answer for the live one. Next drill: boot the restored applications on an isolated network, not only check their data.
Each looked fine until it didn't. For each: what was wrong, how I found it, the fix, and the proof the fix holds.
memory · hypervisor · 10-26 to 10-29
The usual fixes treated the symptom. Per-container counters found the one app causing 86 % of it.
self-heal · 10-22
Automation that guesses is dangerous. Now it checks who really owns an address before it acts.
disk-pressure cleanup · 10-30
Caught a cleanup tool a month before it would have started deleting the wrong things.
Each of these was a choice with a reason, not a default I inherited.
what it demonstrates
Virtualization, storage design, backup and recovery discipline, monitoring that checks itself, safe automation, and root-cause analysis with real counters. Same control mindset as the day job, applied at home.