# Gitea Helm Chart v12 Deployment — Session 2026-07-14 ## Context Deploying Gitea on K8s via ArgoCD using the official `gitea-charts/gitea` chart v12.6.0 (app v1.26.1). DB = external MariaDB Galera via MaxScale VIP 10.0.30.70:3306. Secrets via 1Password ESO. ## Issue 1: DB Password Not Reaching Init Container ### Symptom ``` [F] Failed to initialize ORM engine: Error 1045 (28000): Access denied for user 'gitea'@'10.0.30.62' (using password: NO) ``` ### Root Cause The `configure-gitea` init container runs `gitea migrate` which reads `app.ini`. The chart generates `app.ini` from `gitea.config` values — but `PASSWD` was not set there. Env vars `GITEA__database__PASSWD` override app.ini at runtime, but only if they reach the init container. ### Failed Attempts 1. `gitea.env` — not a valid chart value path; silently ignored 2. `gitea.database.existingSecret` — not a valid chart value; ignored 3. `gitea.config.database.PASSWD: __placeholder__` — literal string reaches DB, not a secret reference ### Solution `gitea.additionalConfigFromEnvs` — templated into ALL containers (init-directories, init-app-ini, configure-gitea, main): ```yaml gitea: additionalConfigFromEnvs: - name: GITEA__database__PASSWD valueFrom: secretKeyRef: name: gitea-db key: password ``` ### Verification After fix, init-app-ini log showed `database + PASSWD` in config, and configure-gitea logged `PING DATABASE mysql` followed by successful schema migration. ## Issue 2: Valkey Auto-Deployed ### Symptom 3 `gitea-valkey-cluster-*` pods appeared despite `redis-cluster.enabled: false`. ### Root Cause Chart v12 renamed `redis-cluster` → `valkey-cluster`. The old key no longer controls the cache subsystem. ### Fix ```yaml valkey-cluster: enabled: false ``` ArgoCD pruned the Valkey StatefulSet after the updated values synced. ## Issue 3: RWO PVC Blocks Rolling Update ### Symptom New pod stuck in `Init:0/3` with: ``` Multi-Attach error for volume "pvc-..." Volume is already used by pod(s) gitea-OLD_POD ``` ### Root Cause `ReadWriteOnce` PVC + single replica + ArgoCD selfHeal creates new pod before old one terminates. Both compete for the same volume. ### Fix ```bash kubectl scale deploy -n gitea gitea --replicas=0 sleep 15 kubectl scale deploy -n gitea gitea --replicas=1 ``` ## Issue 4: Galera Schema Migration Slowness ### Symptom `configure-gitea` init container stayed at `Init:2/3` for 5+ minutes. ### Diagnosis - 104 tables created by Gitea 1.26.1 - Galera replicates each CREATE TABLE + index synchronously to 3 nodes - `SHOW PROCESSLIST` on Galera showed `creating table` / `Committing alter table` - Total time: ~8 minutes from first table to pod Running ### Key Insight This is normal for Galera — do NOT CrashLoop the pod or delete it. Monitor progress via `SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='gitea'`. ## ArgoCD App Architecture Used Two separate ArgoCD Applications: 1. `gitea-config` (sync-wave 1): Git source → `clusters/main/gitea/` (namespace.yaml + external-secrets.yaml) 2. `gitea` (sync-wave 2): Helm source → `https://dl.gitea.com/charts/` chart `gitea` v12.6.0 The root App-of-Apps (`clusters/main/apps/`) auto-discovers both. ## Final Working Values (Key Sections) ```yaml gitea: config: database: DB_TYPE: mysql HOST: 10.0.30.70:3306 NAME: gitea USER: gitea additionalConfigFromEnvs: - name: GITEA__database__PASSWD valueFrom: secretKeyRef: name: gitea-db key: password existingSecret: name: gitea-security keys: internal_token: internal_token jwt_secret: jwt_secret secret_key: secret_key lfs_jwt_secret: lfs_jwt_secret mysql: enabled: false postgresql: enabled: false postgresql-ha: enabled: false redis-cluster: enabled: false valkey-cluster: enabled: false persistence: enabled: true storageClass: ceph-hdd-replica size: 10Gi accessModes: - ReadWriteOnce ``` ## Repo Migration via Gitea Migration API (Task 7) ### Prerequisites - API tokens on BOTH Gitea instances (source CT108 + dest K8s) - Orgs and users created on destination before migrating - `migrations.ALLOWED_HOST_LIST` configured (see above) ### Token Generation ```bash # CT108 (source, LXC — Gitea runs as user 'gitea'): ssh root@10.0.30.105 "su - gitea -s /bin/bash -c \ 'gitea admin user generate-access-token -u dominik -t migrator --raw \ --config /etc/gitea/app.ini'" # K8s Gitea (destination): kubectl exec -n gitea deploy/gitea -- \ gitea admin user generate-access-token -u dominik -t migrator --raw \ --config /data/gitea/conf/app.ini ``` ### Migration Script Pattern ```python for repo in repos: migrate_data = { "clone_addr": f"{CT108_URL}/{owner}/{name}.git", "repo_owner": owner, "repo_name": name, "mirror": False, "wiki": True, "issues": True, "labels": True, "milestones": True, "pull_requests": True, "releases": True, "service": "gitea", "auth_token": CT108_TOKEN, } resp = k8s_api("POST", "/api/v1/repos/migrate", migrate_data) ``` API calls go through `kubectl exec` on the K8s Gitea pod (no LoadBalancer/Ingress needed during migration): ```bash kubectl exec -n gitea deploy/gitea -- curl -s -X POST \ -H "Authorization: token TOKEN" \ -H "Content-Type: application/json" \ -d '{...}' http://localhost:3000/api/v1/repos/migrate ``` ### Results (23 repos) - 18/23 migrated on first attempt ✅ - 1/23 skipped (already existed from test) ⏭️ - 4/23 failed with PR import errors → retried with `pull_requests: false` → all ✅ - Final: 23/23 repos migrated with issues + wikis ### Failed Repos (PR Import Bug) - `dominik/hermes-skills` — `initRepository: getRepositoryByID` - `pm-infra/homelab-board` — `error while listing pull requests` - `d.schoen/packer-proxmox` — `initRepository: getRepositoryByID` - `codecamp-n/qr-reader-webapp` — `error while listing pull requests` Fix: `DELETE /api/v1/repos/{owner}/{name}` (cleanup), then retry migration with `pull_requests: false`. ## Commits - `48a26e6` — Initial scaffold (ArgoCD apps + namespace + ES + helm values) - `68eda13` — Fix: GITEA__database__PASSWD env + disable valkey-cluster - `effb49b` — Fix: use additionalConfigFromEnvs (correct chart mechanism) - `40da393` — Fix: allow migration from CT108 host (wrong setting name) - `68adfab` — Fix: correct migrations setting names (ALLOWED_HOST_LIST + ALLOW_LOCALNETWORKS)