Declarative infrastructure on bare metal · 2026

Homelab

A push to an application repository ends as a public HTTPS response with no manual step in between: Jenkins tests and builds, the image is pinned by sha256 digest in GitHub Container Registry, a commit to the gitops repository describes the desired state, and Argo CD syncs it into the cluster. Terraform provisions, Ansible configures, and WireGuard is the only way into the internal network.

RoleDesign, provisioning, and operation

Diagram of the delivery chain: repository push, Jenkins, image by digest, GitOps repository, Argo CD, and HTTPS response.

Context and evolution

The system behind the screens.

A single OVHcloud server holds thirteen guests, a Kubernetes cluster, and the delivery path for everything I publish. Proxmox splits the hardware, Kubernetes runs the workloads, and a Git repository describes what should be running; between a push and a public HTTPS response there is no manual step left.

The division of responsibility is explicit. Terraform creates resources and keeps its state in a remote bucket another stack created first; Ansible owns everything that happens inside a guest, with the inventory regenerated from Terraform output. Long remote-exec scripts were left out: they exit 0, the resource reports success, and state stops describing reality.

The design changed most when I rebooted the server. The first reboot brought everything back except the tunnel, and the cause was hand-built systemd units loading their own nftables tables on every boot; no test had ever seen them because each Ansible run deleted them before measuring. Since then every recovery procedure records whether it was ever run against this cluster, and the second reboot brought all thirteen guests back with nothing touched.

Decisions and outcomes

  • Terraform provisions and Ansible configures, with no remote-exec scripts that report success just for exiting 0.
  • A three-member control plane with etcd, a PostgreSQL primary with a streaming standby, and snapshots stored off the server.
  • Secrets live in Bitwarden Secrets Manager and never pass through a command line.

System map

Open every application.

These are not labels: every module retains its own route and technical context.

  1. 01

    Provisioning

    Five Terraform stacks with state kept off the server.

    View module
  2. 02

    Configuration

    Ansible owns everything that happens inside a guest.

    View module
  3. 03

    Kubernetes cluster

    A three-member control plane and a Gateway that terminates TLS.

    View module
  4. 04

    Delivery chain

    From a push to an HTTPS response with no manual step.

    View module
  5. 05

    Secrets and access

    One secrets store and one door in.

    View module
  6. 06

    Data and recovery

    A primary, a streaming replica, and runbooks that record what was actually run.

    View module
  7. 07

    Observability

    Prometheus, Loki, and Grafana outside the cluster they watch.

    View module