- Go Template 73.9%
- Shell 19%
- Nix 4.5%
- Makefile 2.6%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .devcontainer | ||
| .github/workflows | ||
| .woodpecker | ||
| charts | ||
| docs | ||
| scripts | ||
| .gitignore | ||
| AGENTS.md | ||
| flake.lock | ||
| flake.nix | ||
| Makefile | ||
| README.md | ||
| renovate.json | ||
Homelab Platform
Note: This project powers my personal homelab. It is shared as a reference and starting point, not as a supported product. Breaking changes happen without warning and versions are not guaranteed to be stable. If you want to use this, fork it rather than depending on it directly.
A turnkey Kubernetes platform built with Helm and ArgoCD. Provides a complete, standards-based foundation for running applications on Kubernetes — from a single-node homelab to a production cluster.
What you get: GitOps deployment, service mesh with ingress, TLS certificates, OIDC authentication, managed databases, monitoring and alerting, security scanning, encrypted backups, and DNS management — all controlled through feature flags in a single values file.
What you deploy on top: Applications using the homelab-common library chart, which abstracts Kubernetes resources into a high-level interface, or raw Kubernetes manifests when you need full control.
Helm Repository
All charts are published to a Helm repository:
helm repo add homelab https://mortenolsen.pro/homelab-core/
helm repo update
helm search repo homelab/
Published charts:
| Chart | Description |
|---|---|
homelab/homelab |
Umbrella chart — the entry point for deploying the platform |
homelab/homelab-core |
Operators sub-chart (wave 0) |
homelab/homelab-shared |
Platform resources sub-chart (wave 1) |
homelab/homelab-monitor |
Monitoring stack sub-chart (wave 2) |
homelab/homelab-common |
Library chart for deploying applications on the platform |
Architecture
┌─────────────────────────────────────────────┐
│ homelab (Umbrella Chart) │
│ Feature flags + platform config │
│ Creates ArgoCD Applications │
└──────────────┬──────────────────────────────┘
│
┌───────┴──────────┬──────────────┐
│ │ │
┌───▼────────┐ ┌────▼─────────┐ ┌─▼──────────┐
│ operators │ │ platform │ │ monitoring │
│ (wave 0) │ │ (wave 1) │ │ (wave 2) │
│ │ │ │ │ │
│ ArgoCD │ │ Gateways │ │ Prometheus │
│ Istio │ │ Certs │ │ Grafana │
│ cert-mgr │ │ Databases │ │ Alertmgr │
│ CNPG │ │ Auth │ │ Blackbox │
│ Kyverno │ │ DNS │ │ │
│ ... │ │ Backups │ │ │
└─────────────┘ └──────────────┘ └────────────┘
Charts deploy in sync-wave order: operators first (installing CRDs), then platform resources that depend on those CRDs, then monitoring last.
Quick Start
Prerequisites
- Kubernetes cluster (K3s, Kind, or any conformant distribution)
- ArgoCD installed on the cluster
- Helm 3.x
- A domain with DNS management (Cloudflare for default TLS setup)
1. Add the Helm repo
helm repo add homelab https://mortenolsen.pro/homelab-core/
helm repo update
2. Configure
Create a values file for your cluster:
# my-cluster.yaml
source:
repoUrl: https://github.com/<your-fork>/homelab-core.git
targetRevision: main
platform:
domain: example.com
ip: 10.0.0.1
gatewayAddress: "" # set after first deploy — see note below
email: admin@example.com
timezone: UTC
acme: staging # use "prod" once DNS is verified
features:
gitops: true
serviceMesh: true
certificates: true
auth: true
postgres: true
mariadb: false # disable what you don't need
monitoring: true
security: true
secrets: true
backup: true
dns: true
reflection: true
reloader: true
storage: true
demo: false
backup:
nfs:
server: 10.0.0.2
path: /backups
monitoring:
ntfy:
url: http://ntfy.prod.svc.cluster.local
topic: alerts
3. Deploy
helm template homelab homelab/homelab -f my-cluster.yaml | kubectl apply -f -
Or from a local clone:
helm template homelab ./charts/homelab -f my-cluster.yaml | kubectl apply -f -
4. Set the gateway address (after first deploy)
After the initial deploy creates the Istio gateway Service, set platform.gatewayAddress to its ClusterIP:
kubectl get svc -n istio-ingress gateway -o jsonpath='{.spec.clusterIP}'
Add this to your values file and re-deploy. This enables STATIC ServiceEntry resolution, which prevents mesh-internal DNS hairpinning without generating DNS queries from every Envoy sidecar. Without this value, ServiceEntries are omitted and mesh-internal routing via public hostnames won't work (external routing is unaffected).
ArgoCD takes over from here — it creates the sub-chart Applications and syncs them in order.
4. Verify
kubectl get applications -n argocd
All applications should show Synced and Healthy within a few minutes.
Features
Each feature flag controls one or more operators and the platform resources that depend on them.
| Feature | Flag | Operators | Platform Resources |
|---|---|---|---|
| GitOps | gitops |
ArgoCD | — |
| Service Mesh | serviceMesh |
Istio base, istiod | Ingress class, gateway pod, public/private Gateway CRs |
| TLS Certificates | certificates |
cert-manager | Cluster issuer (Cloudflare DNS01), wildcard certificate |
| Authentication | auth |
Authentik operator | Authentik server, OIDC provider |
| PostgreSQL | postgres |
CloudNative-PG, postgres operator | Managed PostgreSQL cluster |
| MariaDB | mariadb |
MariaDB operator + CRDs | — (databases created by apps) |
| Monitoring | monitoring |
— | Prometheus, Grafana, Alertmanager, blackbox exporter |
| Security | security |
Falco, Trivy, Kyverno | Runtime detection, vulnerability scanning, policy engine |
| Secrets | secrets |
Sealed Secrets, External Secrets | — (secrets created by apps) |
| Backup | backup |
VolSync | Restic-based PVC backup to NFS |
| DNS | dns |
DNS operator | Pi-hole, DNS sidecar |
| Reflection | reflection |
Reflector | Cross-namespace secret/configmap mirroring |
| Reloader | reloader |
Reloader | Auto-restart pods on config changes |
| Storage | storage |
— | Local-path storage class configuration |
See Features for detailed documentation including dependency graph and configuration options.
Overriding Defaults
For fine-grained control, use the overrides section to pass values directly to any sub-chart:
overrides:
operators:
operators:
istiod:
version: "1.26.0"
platform:
resources:
postgresCluster:
instances: 3
storage:
size: 50Gi
monitoring:
monitoring:
prometheus-operator:
values:
prometheus:
prometheusSpec:
retention: 60d
Deploying Applications
The platform publishes homelab-common, a Helm library chart that provides high-level abstractions for deploying applications. A minimal app needs three files:
Chart.yaml:
apiVersion: v2
version: 1.0.0
name: my-app
dependencies:
- name: homelab-common
version: ">=0.1.0"
repository: https://mortenolsen.pro/homelab-core/
templates/common.yaml:
{{ include "common.all" . }}
values.yaml:
image:
repository: ghcr.io/org/my-app
tag: latest
subdomain: myapp
container:
port: 8080
healthProbe:
type: httpGet
path: /health
service:
port: 80
virtualService:
enabled: true
gateways:
private: true
Then build and deploy:
helm dependency build
helm template my-app . --set globals.domain=example.com | kubectl apply -f -
The common library handles Deployment, Service, VirtualService, PVC, DNS, OIDC, database provisioning, secrets, backups, and monitoring probes — all driven by values.
What the common chart provides
| Template | Resource | Triggered by |
|---|---|---|
common.deployment |
Deployment | image + deployment |
common.service |
Service(s) | service |
common.pvc |
PersistentVolumeClaim(s) | persistentVolumeClaims |
common.virtualService |
Istio VirtualService | virtualService.enabled |
common.serviceEntry |
Istio ServiceEntry | virtualService.enabled |
common.dns |
DNSRecord | dns.enabled |
common.oidc |
AuthentikClient | oidc.enabled |
common.database |
PostgresDatabase | database.enabled |
common.externalSecrets |
ExternalSecret + Password | externalSecrets |
common.probe |
Blackbox Probe | service (auto) |
common.backup |
VolSync ReplicationSource | PVC with backup: true |
Use {{ include "common.all" . }} to render everything automatically, or include individual templates for more control.
Local Development
This repo uses a Nix flake for development dependencies. With Nix and direnv:
cd homelab-core # direnv activates the Nix shell
make recreate # Create Kind cluster + install ArgoCD + deploy stack
ArgoCD UI: localhost:30080 (password in argo-password.txt).
See Local Development for details.
Documentation
| Document | Description |
|---|---|
| Architecture | Chart hierarchy, sync waves, template patterns |
| Features | Detailed feature reference with dependencies |
| Networking | Istio gateways, VirtualServices, DNS, TLS |
| Authentication | Authentik OIDC setup and integration |
| Secrets | External Secrets, Sealed Secrets, Reflector |
| Monitoring | Prometheus, alerts, Grafana, Blackbox probes |
| Backups | Volsync + restic, schedules, restore procedures |
| Disaster Recovery | Full cluster restore runbook |
| Local Development | Nix shell, Kind, Makefile targets |
Troubleshooting
# Check all applications
kubectl get applications -n argocd
# Inspect a specific application
argocd app get <name>
# Check operators
kubectl get applications -n argocd | grep -v Synced
# Certificate issues
kubectl get certificates -A
kubectl describe certificate -n shared wildcard-tls
# Render charts locally to debug
helm template homelab homelab/homelab -f my-cluster.yaml
helm template shared ./charts/shared