| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
My personal Kubernetes platform, in the open. Everything the cluster runs is described as files in this repository, and changes go live by being merged here rather than by anyone running commands against the cluster — that pattern is called GitOps, and Flux is what applies it.
This is a working system rather than a product: it is shaped around what I run, and it is not packaged for reuse. Look around anyway — if you are building something similar, the repository layout and the guides in docs/ are the useful parts, and docs/TEMPLATING.md lists exactly what a fork has to change. 🙌🏻
For local development:
For the production cluster:
Important
The committed secrets are encrypted with my keys, so you cannot decrypt them. To run this locally or in your own Hetzner project, swap in your own:
To run this cluster locally, simply run:
ksail cluster create
ksail workload push
ksail workload reconcilePorts 80 and 443 are mapped to localhost for you (via extraPortMappings in ksail.yaml), and the hosts file points the *.platform.lan names at 127.0.0.1 — so deployed services open in a browser.
The local cluster is a thin test-bed: somewhere to try one component before promoting it to production, not a copy of production. It starts with core infrastructure only — networking and gateway, DNS, TLS, Flux, policy, vertical pod autoscaling, secrets, single sign-on, and the CloudNativePG operator. Note that the operator is installed but no database is created: apps are opt-in, so a plain local bring-up has no PostgreSQL instance running.
Everything heavier (observability, request-rate autoscaling, backup, runtime security, the VM stack, …) and all apps are opt-in. Uncomment what you want in these files — each carries a copy-paste template — then re-run ksail workload push && ksail workload reconcile:
To tear down:
ksail cluster deleteBefore pushing, validate manifests with schema-aware checks and Flux variable substitution:
# Validate local cluster manifests (default)
ksail workload validate
# Validate prod cluster manifests
ksail --config ksail.prod.yaml workload validateThis is faster than a full cluster test and catches YAML errors, missing fields, and broken kustomize overlays.
Local development cluster running on Docker via KSail. Uses Talos with the Docker provider. A small, thin manual test-bed (see Usage) — bring up a component, try it, then promote it to prod.
Cloud cluster running on Hetzner Cloud via KSail's native Hetzner provider. Deployed via v* tags through the CD pipeline, and validated in the merge queue via the CI pipeline.
A high-level inventory of what Flux reconciles onto the cluster. The manifests live under k8s/bases/infrastructure/ and k8s/bases/apps/, with provider-specific pieces (Hetzner CCM/CSI, Longhorn, external-dns, …) under k8s/providers/. The exact set is overlay-dependent: the Hetzner/prod overlay deploys the full base set described below, while the local (docker) overlay is a thin manual test-bed that ships only the core controllers by default and makes the rest opt-in.
Infrastructure
Apps (k8s/bases/apps/)
The cluster uses Flux GitOps to reconcile the state of the cluster with the single source of truth stored in this repository and published as an OCI image. KSail is used for local development, CI/CD testing, and production deployments. For prod, nodes are provisioned on Hetzner Cloud by KSail's native Hetzner provider, which also installs the Hetzner CCM and CSI drivers.
All environments use the Talos Kubernetes distribution. Local development and CI use Talos with the Docker provider; prod uses Talos with the Hetzner provider.
The cluster configuration is stored in the k8s/* directories where the structure is as follows:
Two things stack up. First, each environment points at a provider, which patches the shared resources — so a change can be made for one cluster, for one provider, or for everything at once:
graph LR
subgraph "Cluster-specific"
local["clusters/local"]
prod["clusters/prod"]
end
subgraph "Provider-specific"
docker["providers/docker"]
hetzner["providers/hetzner"]
end
subgraph "Shared"
bases["bases/*"]
end
local --> docker
prod --> hetzner
docker --> bases
hetzner --> bases
Second, Flux applies those layers in order, each waiting for the one before it to come up:
graph TB
bootstrap["bootstrap"]
controllers["infrastructure-controllers"]
infra["infrastructure"]
apps["apps"]
controllers -- "depends on" --> bootstrap
infra -- "depends on" --> controllers
apps -- "depends on" --> infra
Each layer in that chain has a matching folder under providers/<provider-name>/ and bases/. The infrastructure layer, for example, is backed by k8s/providers/<provider-name>/infrastructure/ and k8s/bases/infrastructure/.
The layer definitions themselves are written once in k8s/clusters/base/ with placeholders where the cluster and provider names go; each cluster's overlay fills those in. Only the per-cluster bootstrap/ directory holds manifests unique to one cluster.
Deeper guides and design notes live in docs/:
Note
Prices are approximate and may be outdated.
| Item | No. | Per unit | Total in Actual | Total in $ |
|---|---|---|---|---|
| Cloudflare Domains | 2 | $0,87 | $1,74 | $1,74 |
| Hetzner CX33 (prod) | 6 | €6,49 | €38,94 | $44,21 |
| Hetzner Cloud LB LB11 (prod) | 1 | €5,39 | €5,39 | $6,12 |
| Total | $52,07 |
| Back | FazBrowse Home | New Git URL |