Ansible-orchestrated homelab spanning 42 services across Raspberry Pi devices, Docker homelab, and GPU-accelerated AI lab infrastructure
42 containerized services across the Docker homelab, plus a Raspberry Pi fleet with OLED status displays, GPIO controls, and self-hosted CI agents
In-house apps: Karakuri AI dashboard, Tegata SSO portal, Mezame break-glass control, Hakobi e-reader courier, and Kamado firmware shelf, plus purpose-built infra services
12 autonomous lab members with 57 tools coordinating LLM inference, speech in and out, and image generation
An autonomous issue-to-merge loop: karakuri polls enrolled repositories, hands one Stage of one issue at a time to a headless coding agent, and Kongroo reports on whichever channel fits where Janis is
Eight firmware projects on ESP32 and nRF52 boards — desk displays, a photo frame, a deck companion, a pocket pet — with host-tested cores, CI-compiled sketches and over-the-air updates from the Kamado shelf
A solar-powered, air-gap-capable bug-out replica of the homelab's Tier-0 services — 15 minutes from decision to wheels rolling
Automated infrastructure deployment via Woodpecker CI with idempotent Ansible playbooks and full Jinja2 templating
Three-tier infrastructure | Edge devices → Docker homelab → GPU lab | Plus a portable off-grid replica
42 containerized services | 11 categories | Docker Compose across the Pi fleet + homelab server
System status on a 128x32 I2C display, fitted to two of the three Pis. A button press lights it for fifteen seconds with hostname, IP address, CPU load and memory usage; between presses it stays dark.
One button, read by hold length, for power actions without SSH. The display names the action it is about to take and asks for the release, so a hold can always be backed out of by holding longer.
Self-hosted CI agent running on Pi devices. Executes Ansible deployments, syntax checks, and automated infrastructure updates.
In-house built services | TypeScript + NestJS + Vue 3 | Monorepo architecture
AI workshop dashboard coordinating 12 lab members across LLM inference, TTS synthesis, and image generation. Features a live avatar system with 15+ emotional states.
User management portal for Authelia SSO. Provides admin CRUD for users and groups, an invite-based registration flow for onboarding new users, and self-service profile and password-reset flows, with a preview of exactly which services a given group grants before the invite goes out.
Break-glass dashboard for the Operation Skuld bug-out kit. One armed button — GO LIVE — turns a quietly syncing basement box into a self-contained air-gapped homelab; RETURN TO PREP brings it home. Orchestrates services first, router last, over SSH.
Minimal custom Prometheus exporter for GPU metrics. Uses pynvml directly for accurate per-GPU memory, temperature, utilisation and power readings, serving a standard /metrics endpoint at port 9835 with a 5s scrape interval. Karakuri reads it for its own GPU state and the desk tachometer's dial.
Tiny Prometheus exporter for Docker's own per-container restart count, the one signal a crash-looping container gives that a health check does not. Deployed beside every Docker host's node-exporter and cAdvisor, reading container inspect payloads through a read-only socket proxy so it carries none of the proxy's write or exec permissions.
Custom FastAPI wrapper around the qwen-tts library exposing an OpenAI-compatible /v1/audio/speech endpoint. Supports three synthesis modes with voice prompt caching for efficient voice cloning across lab members.
Always-on courier between the homelab libraries and the e-ink readers. Watches a Grimmory shelf and a Suwayomi category per reader, converts what it finds into the reader's own XTC format, stages it in a per-reader outbox laid out like the SD card, and carries it over either wire — pushed over Wi-Fi whenever a reader shows up on the network, or written straight onto the reader's memory card by the browser when it is plugged in. Never writes back to either library.
.xtch via a vendored cbz2xtc.xtch, one Telegram summary per visitFirmware shelf and flasher for the bench's WiFi boards — the CYD displays and Lumanode alike. Holds one current binary per project and Target, put there by CI on every merge that changes the project or by the bench for a work-in-progress build, keeps a registry of the boards on the LAN with what each one is running, and flashes a chosen project onto a chosen board over the board's own HTTP update route with live progress. Refuses the combinations that would go wrong: a pinned board takes only its own project, and an image built for one Target never goes onto a board registered with another. Never compiles anything.
Self-hosted server behind the e-ink panels around the house — four TRMNL-class Devices today, from a 7.5" workshop board to a fridge-sized Sticky and a pocket Xteink photo frame. Each Device cycles through its own Rotation of Screens: Liquid-templated Plugins that poll the homelab's APIs, a two-slot mashup for the kids' countdowns, photos pulled from the family's Immich library. Developed as its own open-source repository and deployed here from the Ansible inventory like every other container.
Deterministic build of a family-facing continuity handout from one YAML source of truth — served on the homelab with a printable offline copy. No LLM and no secrets anywhere in the build path; the only AI step is a human-in-the-loop review interview that fills the gaps a repo can't know.
The site you are reading. A static reference regenerated from the Ansible inventory on every docker-host deploy — service categories, the lab roster and Skuld status are rendered from Handlebars templates by scripts/generate-docs.ts, then copied onto the homelab for a stock nginx container to serve. Public by design — everything on it is meant to be read.
Firmware and enclosures authored here | Arduino-cli + OpenSCAD | Compiled by CI, finished at the bench
A project lands in the repo the moment there is firmware to compile or an enclosure to model — pinned toolchains, gitignored secrets, and a pure decision core with host tests standing in for a display or a radio no CI container has. Everything else physical in this homelab — the Pis, the GPU hosts, the Mesh's Meshtastic nodes — is bought, racked, or flashed with someone else's image, and lives in the Ansible inventory instead. CI compiles every changed sketch and puts each merged build of a WiFi board's project on Kamado's shelf, but every project still ends in a step only a human at a bench can take: holding BOOT while plugging in a board, calibrating a touch panel by finger, checking a print actually fits. Only a project's pure core — a decay curve, a font table, a draw weighting — and its host test are ever handed to an agent.
Wall-mounted ESP32 ambient light on an 8×8 RGBW panel. Wired into a 12V supply with no cable within reach, so it updates and logs entirely over WiFi. The reference implementation for every hardware convention: pinned build.sh, a pure state-machine core with host tests, and cheap, measured on-device feedback during an update.
The Lab at a glance on a CYD touch display: one row per lab member with avatar, name and state, live VRAM in the header, and a row of preset buttons that start a prompt without typing. A thin client — Karakuri stays the brain, the board polls a bearer-token API over TLS and posts back.
lib/cyd-touchThe GPU box as a dial: a needle for utilisation, a ring for VRAM, temperature and power as digits, and the loaded model's name along the bottom. It swings when llama-server or ComfyUI is working and rests when idle. A gauge, not a control surface — no touch, no buttons.
lib/poll-backoffA VA-11 HALL-A clock that turns into a now-playing card. Idle, it is a dithered Glitch City night with a seven-segment clock; when a Home Assistant media player starts, it becomes a card with cover, title, artist, album and progress. A tap pauses, a swipe skips, and the clock face ignores touch.
lib/tft-textAn Immich photo frame on a shelf. Asks Immich for a photo at random — from one album or the whole library — draws its preview across the panel and keeps it for a few minutes with the date and place on a dimmed band. A tap skips; a long press favourites it in Immich and the heart fills.
lib/tjpgd)lib/cyd-touchThe Lab's avatar on her own screen. Kongroo drawn large, wearing whatever mood the Lab is in — thinking, busy, asleep, under pressure — with a speech bubble holding the last thing she told you and how long ago. Work that ends earns a moment of her success face. No touch, no buttons: a companion, not a control.
An offline deck companion for the game table: tap to draw, read the line for the current danger level, keep track of what is permanently in play. The firmware is a deck-pack engine that knows nothing about any one game — the microSD carries one JSON pack per game, five so far.
A pocket pet for a six-year-old on a Seeed XIAO 1.14" display — fully offline, three stats that decay in real time, three icons above the two front buttons, shake-to-dance, and a screen that sleeps until the egg is picked up. What it grows into is a family tree of fourteen equally likely endings shaped by how it is raised.
lib/xiao-nrf52An animated badge on the Seeed XIAO 0.96" display: the fox-on-vinyl mark spinning above the handle, a card with name and tagline, a QR code to the website, and a status page. Showpiece on the desk on USB; on its LiPo it sleeps after two still minutes and wakes when picked up.
Board wiring, dimensions and driver quirks live once in hardware/boards/ and are linked from every project that uses that board, rather than being restated per project. Two families so far: the CYD ESP32 touch display in both its panel variants, and the Seeed XIAO nRF52840 with its two display sizes.
Firmware shared by two or more projects moves to hardware/lib/ on its second use, never its first. Six libraries so far, each written by one project and moved when the next one needed it.
wifi-upkeep — what every WiFi board needs first: the OTA update route, the log stream, the unit identity and the TLS transportcyd-touch and poll-backoff — the CYD's touch calibration, and when a board polls next or calls a service offlinetft-text and tjpgd — ASCII fold and wrap for the panel fonts, and a JPEG decoder patched for Immich's previewsxiao-nrf52 — panel, backlight, battery gauge and IMU wake for the two XIAO display projects12 autonomous lab members | 57 tools across 24 skill modules | GPU-accelerated inference, TTS, and image generation
Karakuri is a control panel for GPU-accelerated AI services. Manual service control with live monitoring—start/stop containers as needed within VRAM limits. Features an animated Live2D avatar, autonomous lab members with a delegation protocol, scheduled work, a nightly pentest with tracked findings, and a bearer-token surface for the desk boards. Its configuration is git-sourced: the lab workspace is mirrored from this repository on every deploy, and the UI reads it rather than editing it.
Autonomous issue-to-merge loop | karakuri drives, the agent works, Kongroo reports | Presence-aware Reach-outs
Projects enrolls a GitHub repository together with a policy — which label marks an issue as takeable, how often to look, how much one pass may do — and then works it without being asked. karakuri polls the repository, judges which issues are takeable, decides the Stage each one needs, and hands exactly one Stage of one issue to a headless Claude Code invocation at a time. The agent never chooses its own Stage and never merges: a clean review earns a squash-merge that karakuri performs itself. Whatever needs a human — a judgment call, a Stage that struck out twice, an outside author's issue — parks the issue under a label and reaches Janis through Kongroo, on whichever channel fits where he is.
Grab-and-go off-grid homelab | PREP ⇄ LIVE dual-mode | Same hostnames, zero reconfiguration
A solar-powered, air-gap-capable subset of the homelab in a hard case: mini PC, travel router, power station. In PREP mode it sits in the basement quietly pulling the family's Tier-0 data every night. In LIVE mode it becomes the entire homelab — serving the real *.phyberapex.de hostnames with the real wildcard cert, so every family device works with zero reconfiguration. Orchestrated by Mezame, the break-glass dashboard. 15 minutes from decision to wheels rolling.
rrsync -ro key — only the listed trees, each with its own scoped delete, so kit-local data (field photo uploads, backup config) is untouchable by the sync. Immich arrives as a consistent nightly pg_dump instead of a torn copy of a hot datadir; the real wildcard cert is extracted from Traefik's ACME store the same night.
Woodpecker CI | Ansible automation | Git-triggered deployments
Four Docker agents pick up work. Which agent a workflow lands on is decided
by the labels it declares — an unlabelled workflow runs on any agent that will
accept it. Deploy workflows ask by capability rather than by hostname:
an agent advertises may_deploy_* for the targets it is allowed to
converge, and never for the host it runs on. Converging a host restarts the
agent living on it, which would kill the very job doing the converging — so
phonewave deploys the Pis, a Pi deploys phonewave, and neither can deploy
itself. Workflows are also split into two tiers: the heavy ones that run a
full pnpm install, and the light ansible or SSH jobs that
declare ci_tier: small and can therefore also use the
smallest Pi.
Too small for this repo's builds, so the
! prefix makes it opt-in: a workflow lands here only if it
explicitly asks for ci_tier: small. The light ansible and
SSH jobs do, and actually prefer it — an exact label match outranks a
wildcard, which keeps the roomier Pi free for the heavy build.
The roomiest Pi, and the only one with two slots: a single slot would leave it idle whenever the nine-minute docker-host deploy holds it while light-tier work queues behind.
A gated chain. Each deploy diffs its own
refs/deployed/* ref first, so a change that doesn't touch a
target is a no-op skip rather than a redeploy.
Flat — everything runs in parallel with no gate.
syntax-check is push-only, so nothing blocks anything here.
Nothing deploys; the dry-runs report a diff without touching a target.