12 apps reconciled from Git

Git-reconciled · last verified August 2026

I build reliable platforms that deploy and recover predictably.

Platform engineer running a four-node K3s cluster on a single bare-metal Proxmox host and shipping real products on top of it — using Terraform, Ansible and ArgoCD to make every change reproducible.

Cluster nodes
4
Apps reconciled
12
Services run
20+
Pay → playable
46s

Provisioning path

Bare metal → running app

Fig. 1 — the pipeline Every layer declared in Git
  1. Proxmox One bare-metal host
  2. Terraform + Ansible 5 VMs provisioned, then configured
  3. K3s 4-node cluster, 1 control plane
  4. ArgoCD 12 apps reconciled from Git
  5. Applications 20+ services on MetalLB + Longhorn

Observed by

Prometheus · metrics + custom exporters

Grafana · dashboards per workload

Alertmanager · routed to Discord

Services

3 featured · 3 smaller builds

kubernetes-platform Deployed

Infrastructure

Self-hosted Kubernetes platform

A four-node K3s cluster on one Proxmox host, with every layer declared in Git.

View case study
Problem
Twenty-plus services on one box, every change made by hand. Nothing was reproducible, and a rebuild meant re-deriving undocumented steps.
What I built
Terraform provisions the VMs, Ansible brings up a four-node K3s cluster, ArgoCD reconciles every workload from Git. Plus MetalLB and Nginx Ingress, Longhorn with scheduled snapshots, a cert-manager internal CA for real TLS, and Prometheus/Grafana/Alertmanager with custom exporters alerting to Discord.
Result
12 applications reconciled from Git, drift auto-corrected, promotion by pull request. A rebuild is a pipeline, not a weekend — and the recovery runbook has been tested against real failures.
  • Terraform
  • Ansible
  • K3s
  • ArgoCD
  • Prometheus
Cluster topology

Cluster topology

Proxmox host — one physical machine

  • k3s-master control plane · VM
  • k3s-worker-1 worker · VM
  • k3s-worker-2 worker · VM
  • k3s-worker-3 worker · VM

wings-vm · Docker host, outside the cluster

  • MetalLB + Nginx Ingress · stable addressing
  • Longhorn · replicated storage, scheduled snapshots

Longhorn provides replication across cluster VMs; the physical Proxmox host remains a single failure domain.

Five VMs on one host — four form the cluster, one runs game servers.

Fig. 2 — how one application is wired

Fig. 2 — how one application is wired A resource graph: the homelab repository feeds the vaultwarden ArgoCD application, which owns a deployment, service, ingress, certificate and volume claim; those resolve to a pod, a TLS secret issued by the internal CA, and a Longhorn volume. GIT ARGOCD RESOURCES RUNTIME repo homelab application vaultwarden deployment vaultwarden service vaultwarden ingress · https vaultwarden certificate · cert-manager vaultwarden-tls pvc · longhorn vaultwarden-data pod vaultwarden secret · issued by the internal CA vaultwarden-tls volume · daily snapshot, retain 7 longhorn
One of the twelve: the vaultwarden Application and everything ArgoCD reconciles for it — down to the cert-manager certificate that gives it real TLS and the Longhorn volume it stores data on.
wyrmhost Building

Platform automation

WyrmHost

Game-server hosting that provisions itself: pick a plan, pay, and the server boots.

View case study
Problem
Selling game-server hosting means provisioning a server per order. By hand it doesn't scale, and it can't run overnight.
What I built
Storefront, Stripe checkout, queue worker, and the panel API call that creates and boots the server. The storefront had no provider for the panel I run, so I ported one against its API. Each environment is its own GitOps repo with ArgoCD guardrails and manual promotion to production.
Result
Payment to a live, playable server in approximately 46 seconds, with zero manual steps — including automatic licence acceptance, which previously crashed every new server on first boot.
  • Laravel
  • Stripe
  • Docker
  • ArgoCD
  • REST APIs
Provisioning sequence
  1. Checkout Customer picks a plan and pays
  2. Invoice paid Provisioning job queued
  3. Server created Allocation assigned, files installed
  4. Licence accepted Handled automatically, then start
  5. Online Joinable — ~46s total
Measured end-to-end on a live test purchase.
The WyrmHost storefront: a dark landing page headed “Summon a server. It breathes fire in seconds.”, with a Deploy Now button and a mock deploy console showing a server coming online. View the full-size storefront screenshot (opens in a new tab)
The storefront customers buy from — the front door to the sequence above.
wyrmtable Live

Product · full-stack

Wyrmtable

A real-time tabletop toolkit that keeps a whole table on one shared game state.

View case study
Problem
Tabletop RPG tools run on one machine, so the whole table crowds a single screen. Nothing shared game state live across players' own devices.
What I built
A real-time browser toolkit: shared table state over websockets, server-authoritative dice, combat tracker and character import. I built and run all of it — containerised, versioned migrations, nightly backups with a staleness alert, and its own Grafana dashboards.
Result
Live and public at wyrmtable.eu, behind a Cloudflare Tunnel rather than an open port, with the pod locked down by NetworkPolicy, running non-root and rate-limited on auth. Releases are pinned image tags, promoted by hand.
  • Next.js
  • TypeScript
  • Socket.IO
  • PostgreSQL
  • Kubernetes
Smaller builds 3 tracked
fpl-ai · trading-harness · raid-companion
  • fpl-ai

    Data · ML

    Ingests official Fantasy Premier League data, models expected points and recommends transfers — advice-only by design. Most of the work was data hygiene: unit mismatches and an unreliable upstream column would have quietly poisoned every recommendation.

    • Python
    • Pandas
    • Kubernetes
    Deployed
  • trading-harness

    Research · systems

    Walk-forward backtesting, realistic fee and slippage modelling, risk limits, and alerting that fires on silence as well as errors. The honest result: no edge. Win rate times payoff landed at 1.0 — the signature of random entries. Measuring that and stopping beat another round of tuning.

    • Python
    • Backtesting
    • GitHub Actions
    Research
  • raid-companion

    Desktop · tooling

    A desktop companion for raid nights, shipped as one self-contained executable — users double-click a file instead of reading an install guide.

    • Electron
    • TypeScript
    • Node.js
    Shipped

Incident history

Four incident write-ups

What broke, and what changed after Root-caused and written up
sev-1

Thin pool hit 100% and locked every VM

Failure
The storage pool filled completely. Writes froze and every VM on the host locked up at once.
Root cause
The hypervisor ran with discard=ignore, so TRIM inside the guests never reached the host. Deleted blocks were never reclaimed — the pool only grew, however much the VMs freed.
Fix
Enabled discard pass-through and reclaimed the orphaned extents with fstrim, bringing usage back under the ceiling.
Prevention
Pool utilisation is now alerted on, so the next approach to the ceiling arrives as a warning, not an outage. A setting that silently disables reclamation stays invisible until it's urgent — verify the whole path, not just the guest.
sev-1 An out-of-memory node took itself offline resolved
Failure
A cluster node stopped responding entirely instead of shedding one workload.
Root cause
A memory-hungry workload consumed the node's RAM. With no kubelet eviction thresholds set, the kubelet ran out of headroom before it could evict anything.
Fix
Set explicit eviction thresholds and system reservations so the kubelet always keeps enough memory to act, and right-sized the workload's limits against measured usage.
Prevention
A greedy pod is now a pod problem, not a node problem. Eviction config is part of cluster setup, not something added after the first incident.
sev-2 The NVR forgot everything on every restart resolved
Failure
Recordings accumulated on disk that the application no longer knew existed, and cleanup never ran.
Root cause
The video recorder's database sat on an emptyDir volume. Every pod restart wiped the index while the footage stayed on disk.
Fix
Moved the database onto a persistent volume and sized retention against measured write throughput rather than guesswork.
Prevention
If it survives a restart in your head, it needs a PVC in your manifest. Every stateful workload got the same audit.
sev-2 Intermittent API server timeouts under memory pressure resolved
Failure
Sporadic datastore and TLS timeouts from the API server, with no obvious trigger.
Root cause
The control-plane node was running with almost no free memory and no swap. Its datastore had grown large enough that queries slowed under cache pressure.
Fix
Traced the timeouts back to host memory rather than chasing the TLS errors, then relieved the pressure.
Prevention
Datastore size is now monitored, so growth is visible before it becomes latency. Symptoms surfaced several layers away from the cause — the metric that mattered wasn't the one throwing errors.

Next

Where the platform goes from here

Planned, not built

Reviewed August 2026

  1. 01

    A second physical host

    Longhorn replicates volumes across the cluster VMs, but every one of those VMs sits on the same Proxmox box. Splitting them across two hosts turns a single failure domain into a real one — and buys back the memory headroom I keep having to ration.

  2. 02

    An HA control plane, and backups that leave the building

    Running one control-plane node is the other thing I would fix first. A multi-node control plane, and database backups shipped off-cluster rather than living on the array they are meant to protect.

  3. 03

    Self-hosted inference on hardware that can hold it

    Local model serving came off the cluster once it started competing with everything else for disk and RAM. I want it back on dedicated hardware, sized for the job instead of borrowing capacity from services people actually use.

  4. 04

    AI-assisted operations, rebuilt on n8n

    I ran a version of this already: critical Alertmanager alerts routed into an n8n webhook that called a local model and attempted remediation. I took it out in June, and critical alerts have gone straight to Discord ever since. I want it back with a person in the loop — n8n drafts the diagnosis and the proposed fix, a human approves it, and every run leaves a trace. It needs 03 first; the inference has to sit somewhere it is not competing with production.

  5. 05

    Wyrmtable past a single replica

    Live table state currently lives in one process's memory, so the app runs a single replica. A Redis adapter behind Socket.IO would let it scale out — and a battle map with fog of war is the next feature on the list.

Stack

Used to build and operate something real

Infrastructure

  • Proxmox
  • Terraform
  • Ansible
  • Kubernetes / K3s
  • Helm
  • Docker
  • Linux
  • MetalLB
  • Longhorn
  • cert-manager

Delivery & observability

  • ArgoCD
  • GitOps
  • GitHub Actions
  • Prometheus
  • Grafana
  • Alertmanager
  • Exporters
  • n8n

Application

  • TypeScript
  • Next.js
  • React
  • Node.js
  • Socket.IO
  • Python
  • PostgreSQL
  • Electron

Practice

  • Infrastructure as code
  • Incident root-cause analysis
  • Disaster recovery
  • Capacity planning
  • Observability design
  • Documentation
Contact Usually replies within a day

Need someone who can build the platform and keep it running?

I do both — the provisioning pipeline and the 2am root-cause analysis. Happy to talk platform engineering, Kubernetes, or anything that runs in a homelab.