k3s GitOps infrastructure

The platform serving the page you are reading — Kubernetes in production on a 1.8 GB VPS.

k3sFlux CDSOPS / age cert-managerTraefikGitHub Actions

The problem

One VPS, several applications to host (this site, Coach Adaptatif, more to come), and the wish for a real pipeline: automatic deployment, staging, one-command rollback. The accepted constraint: 1.8 GB of RAM — where most Kubernetes guides recommend 4 GB as a minimum.

Architecture

k3s platform architecture A single 1.8 GB k3s node. Traefik terminates TLS and routes to the site, Coach Adaptatif and Frigo Adaptatif. Each database is reachable only by its own application. Flux CD pulls the desired state from the infrastructure Git repository. k3s cluster · 1 node · 1.8 GB RAM 443 NetPol PVC pull applies the Git state Internet visitors jjoly-infra manifests secrets SOPS Traefik ingress · TLS cert-manager Let's Encrypt Flux CD 2 controllers reconciles jjoly.eu nginx non-root coach.jjoly.eu coach-app · Go fridge.jjoly.eu fridge-app · Go staging.* 0 replicas when idle MariaDB dedicated SQLite 1 DB / household
One node, three exposed services, each database isolated behind a NetworkPolicy.
Text version
Internet → Traefik (ingress) + cert-manager (Let's Encrypt)
  ├── jjoly.eu          → static site (nginx, this site)
  ├── coach.jjoly.eu    → coach-app + dedicated MariaDB
  ├── fridge.jjoly.eu   → fridge-app + SQLite (one database per household)
  └── staging.*         → staging namespace (0 replicas when idle)

Flux CD watches the infrastructure Git repo → applies every change
CI (GitHub Actions): tests → build → Trivy scan → GHCR → commit the tag
GitOps delivery chain GitHub Actions tests, builds, scans the image with Trivy, pushes it to GHCR, then writes the new tag to the infrastructure repository. The cluster pulls that change with Flux: CI has no access to the cluster. GitHub Actions — a third party k3s cluster pull applies git push main → staging vX.Y.Z → prod tests go test build image distroless scan Trivy blocking on critical CVE GHCR pinned image by digest tag commit to jjoly-infra Flux CD pulls from Git production rollback : git revert
CI only writes to Git; the cluster fetches the change itself. No cluster credential ever leaves the cluster.

The choices that matter

Resilience

The result

A push to main updates staging in about a minute; a vX.Y.Z tag deploys to production. Adding an application means duplicating a folder of manifests. This site is the living demonstration.

Cluster state

Measured on 2026-07-29 with outils/mesurer-infra.sh. These figures are not collected by CI: granting the runner cluster access would open a path to production from a third party.

What runs on it

Versions actually deployed, read from the infrastructure repository; HTTP response, response time and TLS certificate observed by CI while this page was being built — on . This is not real-time monitoring: it is a dated observation, with no request made from your browser.
ServiceVersion in productionAt last buildResponse timeTLS certificate
Frigo Adaptatif v4.2.1 returned 200 789 ms valid until 2026-12-13
Coach Adaptatif v0.2.5 returned 200 906 ms valid until 2026-12-11
jjoly.eu sha-bd8fa59 returned 200 859 ms valid until 2026-12-11

The technical notes behind these choices — including the debugging mistakes — are written in French.