The problem
I wanted to run AI workloads locally instead of renting them: chat models, image generation, speech-to-text and small fine-tuning jobs. I also needed hardware video transcoding for my media server.
Those workloads all want a GPU, they run in different containers, and some of them will happily take all of a card's memory. I also didn't want to give every other VM on the hypervisor access to the GPUs.
Architecture
All of it runs on one VM on my XCP-ng host, with the GPUs passed through to it. Everything else on that hypervisor stays GPU-free.
Containers get access through NVIDIA CDI (the Container Device Interface), and I keep a script that enables it. The M60s are older datacenter cards, so I also keep a script for tuning their clocks.
Several services can want the same card, so a GPU lock file coordinates access between them. ComfyUI has an autoheal container that restarts it when its health check fails, plus a guard config for the GPU. Some stacks also have OpenTelemetry compose files for tracing.
Siggy-bot is a Discord voice bot built as separate services:
Speech-to-text is its own small API service, so other projects can use it too. The bot only handles the Discord side. The thinking happens in an n8n workflow, backed by Postgres with pgvector and Redis.
Key decisions
Pass the GPUs to one VM, not to everything. One VM owns the cards, and anything that needs a GPU lives there, whichever of my domains it belongs to. A lock file coordinates jobs so they don't fight over the cards.
Split the bot into services. Separating speech-to-text, the agent and the bot means I can swap the model or workflow without touching the Discord code.
Self-heal the flaky piece. Image generation is the workload most likely to wedge, so it gets automatic restarts instead of me noticing hours later.
Training a model on my own likeness
The one image-generation project worth mentioning: in 2023–24 I fine-tuned early Stable Diffusion models on photos of myself, so they could generate images with my likeness. I trained the full model, not a LoRA. It worked, with results typical of models from that era.
If I did it today, I'd train a LoRA instead. It's a small add-on file rather than a whole new model, it trains faster, and it can be combined with other LoRAs and base models.
Results
- Local LLM chat, image generation and fine-tuning run on my own hardware, alongside media transcoding.
- This started in 2022 with a single AUTOMATIC1111 Stable Diffusion Web UI container. It grew into a dedicated VM with GPU passthrough, CDI and coordinated access.
- Siggy-bot answers voice questions in Discord using my own speech-to-text service and an n8n workflow.
I actually use n8n more at work than at home. There it drives SIS imports, device workflows and IT automation, and an MCP server lets Claude build and edit workflows with me. That's covered in the WM.Tools case study.