Services
Gateway
Haproxy
HAProxy is a free and open source software that provides a high availability load balancer and reverse proxy for TCP and HTTP-based applications that spreads requests across multiple servers.
Haproxy load-balances all incoming http and https traffic from the Internet (ports 80 and 443) onto the traefik host ports published by klipper-lb on every cluster node (workers preferred, masters as backup), in TCP/TLS-passthrough mode — TLS terminates at traefik inside the cluster. It also load-balances Kubernetes api server traffic on the local network (port 6443) onto the master nodes. An ACL rule is defined to accept only local network IP address requests for the api server.
The web interface lets you view the health status of every backend node on both types of endpoints (server api and internet traffic).
Pi-Hole
Pi-hole is a Linux network-level advertisement and Internet tracker blocking application which acts as a DNS sinkhole and optionally a DHCP server, intended for use on a private network. It is designed for low-power embedded devices with network capability, such as the Raspberry Pi, but can be installed on almost any Linux machine.
Pi-hole has the ability to block traditional website advertisements as well as advertisements in unconventional places, such as smart TVs and mobile operating system advertisements.
Using the web interface, you can enable/disable ad and tracker blocking, add a list of domains to be blocked, and configure local network DNS settings (and DHCP if required). It is also possible to view statistics on blocked domains according to the privacy rules set.
Wireguard
WireGuard is a communication protocol and free and open-source software that implements encrypted virtual private networks (VPNs), and was designed with the goals of ease of use, high speed performance, and low attack surface.
Wireguard's web interface lets you create / delete / activate / deactivate VPN users, download their configuration file and display the user's QrCode. With this user configuration file, a user can access the homelab network to perform an ssh connection to the machines and then request the Kubernetes api server.
CrowdSec
CrowdSec is an open-source security engine that analyses logs from various sources (HAProxy, sshd, syslog) and detects malicious behaviour using community-curated scenarios. Detected attackers are blocked at the network level via an nftables firewall bouncer.
The gateway deployment runs the CrowdSec engine as a Docker container with the following collections:
crowdsecurity/linux— SSH brute force, bad user agents, port scanscrowdsecurity/sshd— SSH-specific attack patternscrowdsecurity/haproxy— HTTP abuse through the load balancercrowdsecurity/base-http-scenarios— generic HTTP attacks (scanners, exploits)crowdsecurity/http-cve— known CVE exploit patterns
A separate Kubernetes deployment (CrowdSec Helm chart) provides cluster-wide monitoring via a DaemonSet agent that parses the traefik JSON access logs.
Access
Gateway web interface services are deployed and accessible for admin purpose, they are available on local network at :
| Name | Url |
|---|---|
| Haproxy dashboard | http://192.168.1.99:8404 |
| Pihole dashboard | http://192.168.1.99:5353 |
| Wireguard dashboard | http://192.168.1.99:51821 |
Notes: Replace
192.168.1.99with the gateway's ip address set in hosts.yml.
Kubernetes
Services
The app catalog under argo-cd/apps/ provides the following charts; each instance enables its own subset via the enabled flag in its core.yaml / tenant.yaml catalogs :
| Name | Description | Helm chart |
|---|---|---|
| Actions-runner-controller | Github Actions runners controller | actions-runner-controller/actions-runner-controller |
| ArgoCD | GitOps continuous delivery tool | argo/argo-cd |
| Argo-workflows | Workflow automation engine | argo/argo-workflows |
| Cert-manager | Cloud native certificate management | cert-manager/cert-manager |
| Cloud-native-postgres | Cloud native postgres database management | cnpg/cloudnative-pg |
| Coder | Remote selfhosted development environments | coder-v2/coder |
| CrowdSec | Open-source security engine & threat detection | crowdsec/crowdsec |
| Homepage | Home dashboard | unknowniq/homepage |
| Gitea | Private, Fast, Reliable DevOps Platform | gitea/gitea |
| Harbor | Cloud native registry | bitnami/harbor |
| Traefik | Ingress controller & Gateway API implementation | traefik/traefik |
| Keycloak | Single Sign On service | cloudpirates/keycloak |
| Kyverno | Kubernetes policy engine (admission control) | kyverno/kyverno |
| Longhorn | Cloud native distributed block storage | longhorn/longhorn |
| Mattermost | Chat service with file sharing and integrations | mattermost/mattermost-team-edition |
| MLflow | ML experiment tracking and model registry | community-charts/mlflow |
| Outline | Share notes and wiki with your team | lrstanley/outline |
| Prometheus-stack | Open-source monitoring solution | prometheus-community/kube-prometheus-stack |
| RustFS | High Performance Object Storage | - |
| Sonarqube | Code quality analysis service | sonarqube/sonarqube |
| Sops | Secret manager that decode on the fly | sops-secrets-operator/sops-secrets-operator |
| System-upgrade-controller | K3S upgrade controller | - |
| Teleport | Secure access and identity for infrastructure | teleport/teleport-cluster |
| Trivy-operator | Kubernetes-native security toolkit | aqua/trivy-operator |
| Vault | Secret management service (standalone chart) | hashicorp/vault |
| Vault-operator | Bank-Vaults operator (HA Vault cluster) + VSO | bank-vaults/vault-operator + hashicorp/vault-secrets-operator |
| Vaultwarden | Password management service | vaultwarden/vaultwarden |
Versions
All services helm charts and versions are managed through ArgoCD ApplicationSets with configuration stored in:
- App charts: ./argo-cd/apps/ — each app has its own
Chart.yamldefining the chart version and dependencies. - Per-instance metadata: ./argo-cd/instances/<instance>/instance.yaml — cluster destination, env, repos, AppProject bindings.
- Per-instance app catalog: ./argo-cd/instances/<instance>/core.yaml and tenant.yaml — enable/disable apps + per-app overrides (sync wave, namespace, release name, ...).
- Per-instance values: ./argo-cd/instances/<instance>/values/core/<app>.yaml and tenant/<app>.yaml — values overrides applied on top of the chart defaults.
Management
Services are managed by a two-level ApplicationSet hierarchy declared by the ohmlab chart in the argocd-system namespace:
- The root
managerAppSet discovers each instance folder and emits one Application per instance pointing at theinstance-managerchart. - That chart renders two child AppSets per instance —
core-<instance>(platform tier, bound toadmin-coreAppProject) andtenant-<instance>(apps tier, bound toadmin-tenantAppProject).
To enable or disable a service, edit the relevant entry in argo-cd/instances/homelab/core.yaml or tenant.yaml and flip enabled: "true" / enabled: "false".
Access
Kubernetes services that are available through user interfaces are centralized on the Homepage dashboard. Platform (core) services live under the core. subdomain; tenant apps sit directly under the root domain :
Core (platform)
| Name | Url |
|---|---|
| ArgoCD (core) | https://gitops.core.domain.com |
| Keycloak | https://sso.core.domain.com |
| Longhorn | https://longhorn.core.domain.com |
| Teleport | https://teleport.core.domain.com |
| Vault | https://vault.core.domain.com |
Tenant (apps)
| Name | Url |
|---|---|
| ArgoCD | https://gitops.domain.com |
| Gitea | https://git.domain.com |
| Grafana | https://monitoring.domain.com |
| Homepage | https://domain.com |
| Mattermost | https://mattermost.domain.com |
| RustFS - api | https://s3.domain.com |
| RustFS - console | https://console.s3.domain.com |
Notes: Replace
domain.comby your own domain configured in your values files. Optional catalog apps (Coder, Harbor, Outline, SonarQube, Vaultwarden, ...) follow the same pattern when enabled.
Single sign on
Keycloak is deployed as the cluster single sign-on tool. It provides a single account (username / password pair) that grants access to multiple services, and propagates user groups to control access levels.
Users and access levels are managed via the Keycloak interface (cf. keycloak service url) using the admin credentials from Vault (keycloak.username / keycloak.password under the keycloak secret path).
Don't forget to select the
homelabrealm.
A default admin group grants admin-level access on every connected service; users not in this group get standard access.
Services currently connected through SSO (client secrets are stored in Vault and delivered by VSO):
- ArgoCD (core + tenant instances)
- Gitea
- Grafana
- Longhorn
- RustFS (console OIDC)
- Vault
Optional catalog apps (Coder, Harbor, Outline, SonarQube, ...) ship with the same Keycloak OIDC wiring and join the list when enabled.
Secrets
Secrets are sourced from Vault and synced into Kubernetes by the Vault Secrets Operator (VSO). Each chart that needs secrets depends on the vso-utils subchart, which renders VaultStaticSecret custom resources pointing at a Vault path.
VSO talks to Vault over TLS and validates the server against a vault-ca secret present in every app namespace. That secret is distributed by the Kyverno sync-vault-ca generate policy, which targets namespaces labelled ohmlab.fr/vault-access=true. The label itself is applied declaratively by the instance-manager ApplicationSets (managedNamespaceMetadata), so it survives namespace re-creation. The whole secret chain silently stops if that label disappears — ohmlab check reports failing VaultStaticSecret resources, which is the first symptom.
Each app also gets a dedicated least-privilege Vault policy (homelab/data/platforms/+/+/<app>, read-only) and a Kubernetes auth role bound to the vso ServiceAccount in the app's own namespace — a role bound to the wrong namespace fails with 403 namespace not authorized.
Security policies
Four Kyverno ClusterPolicies guard admissions (see argo-cd/apps/kyverno/templates/):
| Policy | Action | Notes |
|---|---|---|
pod-security-baseline | Enforce | PSS baseline; infra namespaces needing hostPath excluded |
require-non-root | Enforce | runAsNonRoot; nginx/log-reader namespaces excluded |
disallow-latest-tag | Enforce | :latest blocked; internal tooling images excluded |
require-resource-limits | Audit | stays Audit — blocking operator-created pods mid-incident is worse than a report |
Actions are configurable per instance via policies.<name>.failureAction in the kyverno app values. Exceptions are GitOps-managed: the kyverno PolicyException feature is enabled but restricted to the kyverno namespace, so every exception lives in policy-exceptions.yaml and workloads cannot self-exempt. In-cluster traffic is encrypted wherever the component supports it: CNPG PostgreSQL serves TLS and every client connects with sslmode=require; gitea⇄valkey uses password auth over TLS; Vault and argocd-server serve TLS that traefik verifies upstream via BackendTLSPolicy (Vault against its own CA, ArgoCD against its Let's Encrypt cert); Teleport terminates TLS itself behind a Gateway API TLSRoute passthrough.
Monitoring
The cluster itself and the platform services are monitored using Prometheus and Grafana. Every component that exposes metrics ships a ServiceMonitor/PodMonitor (ArgoCD, Vault, Traefik, cert-manager, CrowdSec, Longhorn, Gitea, Keycloak, CNPG, sops, ...) — roughly 30 monitors across the cluster.
Some dashboards are already delivered with the installation but more can be added in argo-cd/apps/prometheus-stack/grafana-dashboards/, they will be automatically loaded on ArgoCD synchronization via the dashboards.yaml template. Already added dashboards are: