k8s-and-chill/CLAUDE.md
Felix Wolf 4e48df73d3 feat(ocis): Transition to oCIS and enhance deployment
Removes the full Nextcloud stack (PostgreSQL/CNPG, Valkey, Caddy) and
  deploys oCIS at drive.tr1ceracop.de. oCIS is self-contained — no
  external database or cache needed.

  Key design decisions:
  - S3ng storage backend on Hetzner Object Storage (ocis-tr1ceracop)
  - Chart fetched via vendir git source (not published to a Helm repo)
  - All secrets generated in-cluster via PreSync init Job (never in git)
  - Memory requests on all pods to prevent node overcommit
  - Persistence on local-path for metadata (idm, nats, search, storage)

  Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 14:01:55 +02:00

4.2 KiB

k8s-and-chill

Project Overview

GitOps-managed Kubernetes cluster on Hetzner Cloud running Talos Linux. Uses myks for Helm chart rendering with ytt overlays, targeting ArgoCD for continuous deployment.

Cluster

  • 3 Talos control-plane nodes (CAX11 ARM64, Hetzner Cloud Nuremberg)
  • Node IPs: 195.201.219.111, 195.201.140.75, 195.201.219.17
  • allowSchedulingOnControlPlanes: true (no dedicated workers)
  • All namespaces use pod-security.kubernetes.io/enforce: privileged

Domain & DNS

  • Domain: tr1ceracop.de (registered at INWX)
  • DNS: Managed at INWX with wildcard A record *.tr1ceracop.de pointing to node IPs
  • Forgejo: https://git.tr1ceracop.de
  • ArgoCD: https://argocd.tr1ceracop.de

Deployed Applications

App Namespace Notes
traefik traefik Ingress controller, DaemonSet with hostPort 80/443
cert-manager cert-manager Let's Encrypt HTTP-01 via ClusterIssuer letsencrypt
forgejo forgejo Git server, SQLite, local-path PVC
argocd argocd GitOps controller
local-path-provisioner local-path-storage Default StorageClass, installed via upstream manifest

myks Structure

prototypes/         # Application templates (helm values + ytt overlays)
  argocd/
  traefik/
  cert-manager/
  forgejo/
envs/
  env-data.ytt.yaml           # Global ArgoCD config
  _env/                       # Shared overlays (annotations, secrets)
  production/
    env-data.ytt.yaml          # App list for production
    _apps/{app}/app-data.ytt.yaml  # Per-app overrides
rendered/
  envs/production/{app}/       # kubectl-ready manifests
  argocd/production/           # ArgoCD Application resources
talos/
  controlplane.yaml            # Talos machine config
  talosconfig                  # Talos client config
  kubeconfig                   # Cluster kubeconfig

Prototype Pattern

Each prototype follows this structure:

  • app-data.ytt.yaml — namespace declaration
  • vendir/vendir-data.ytt.yaml — chart name, version, repository URL
  • vendir/base.ytt.yaml — vendir config template (identical across all)
  • helm/{chart}.yaml — Helm values overrides
  • ytt/ns.ytt.yaml — Namespace resource + namespace overlay on all resources

Key Commands

myks render                          # Render all apps
myks render production <app>         # Render single app
kubectl apply -f rendered/envs/production/<app>/ --server-side  # Deploy

Kubeconfig & Talos

  • KUBECONFIG and TALOSCONFIG are already set in the user's shell environment. Do not set them in commands.

Known Issues / TODOs

  • Namespace race condition: First kubectl apply of a new app often fails because namespace isn't ready. Re-apply once.
  • Traefik DaemonSet updates: Requires updateStrategy.rollingUpdate.maxSurge: 0 because hostPort conflicts prevent surge.
  • Forgejo Ingress API version: Chart renders extensions/v1beta1, fixed via ytt/ingress-fix.ytt.yaml overlay to networking.k8s.io/v1.
  • ArgoCD: Fully wired to Forgejo via App of Apps. Root Application in default project syncs rendered/argocd/production/. Deploy key provisioned automatically by argocd-deploy-key-init Job in forgejo namespace.

Container Images

  • Never use bitnami images. Use alpine/k8s or plain alpine for utility Jobs instead.

Secrets

  • Never commit secrets to git. This is a public repository.
  • All secrets must be generated in-cluster using init Jobs (ArgoCD PreSync hooks) that create secrets if they don't already exist. See prototypes/ocis/ytt/s3-secret-job.ytt.yaml for the pattern.
  • External secrets (e.g. S3 credentials) that cannot be generated must be created manually in the cluster before deploying. The init Job should validate their existence and fail fast if missing.
  • When adding a new application that uses a Helm chart generating secrets, configure all secretRefs to point to pre-created secret names and use an init Job to generate them.
  • Known external secrets (not in git, created manually):
    • ocis/ocis-s3-credentials — Hetzner S3 access key and secret key
    • cert-manager/letsencrypt-account-key — ACME account key (auto-generated by cert-manager)