- New: smart-home/home-assistant-dashboard-conventions (Mushroom cards, view tabs, no Bubble Cards) - Updated: rke2, ceph, galera, proxmox, brainstorming, compound-learning, 1password-cli, smart-home-automation skills - New references: ceph-cluster-administration, docker-volume-forensics, ceph-crush-weight, ceph-ec-mixed-size
6.8 KiB
Hermes Host Git Remote Cutover to K8s Gitea — 2026-07-14
Context
After ArgoCD self-management cutover (Section 4.3e), the Hermes host's
own iac-homelab git remote still pointed to CT108
(10.0.30.105:3000). User instructed: "nutze du intern in hermes ab
jetzt auch das k8s gitea" — switch Hermes host's git remote to K8s Gitea.
Key Learnings
CoreDNS Is K8s-Internal Only
The CoreDNS hosts override (Section 4.3d) makes git.schoen.codes
resolve to 10.0.30.203 only inside K8s pods. The Hermes host,
being outside K8s, cannot use CoreDNS. Public DNS was explicitly
rejected by the user ("Nutze für Internet Zwecke nicht den externen DNS
Resolver, sondern einen internen").
Solution: /etc/hosts entry on the Hermes host:
echo "10.0.30.203 git.schoen.codes" | sudo tee -a /etc/hosts
Gitea API Tokens Do NOT Migrate
When repos are migrated from CT108 Gitea to K8s Gitea via the Gitea
Migration API, API tokens are NOT copied. Tokens are stored hashed
in the database and are instance-specific. The old token
(07efa...) from CT108 returns 401 Unauthorized on K8s Gitea.
New token must be generated on K8s Gitea:
# Via mgmt-runner (VM200) — Hermes host has no kubectl:
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.30.124 \
"KUBECONFIG=/root/.kube/config kubectl exec -n gitea deploy/gitea -- \
gitea admin user generate-access-token \
-u dominik -t hermes-gitops --scopes all --raw"
Note: The --config /data/gitea/conf/app.ini flag is optional —
gitea admin user generate-access-token auto-discovers the config when
running inside the pod. The command outputs the raw token to stdout.
JWT ≠ Gitea API Token (Critical Distinction)
The 1Password ExternalSecrets for Gitea (gitea-security) contain
internal_token, jwt_secret, secret_key, lfs_jwt_secret. These
are internal security tokens (JWTs), NOT API access tokens.
How to distinguish:
- Gitea API token: hex string, e.g.
07efa534ea1a147fce1bf5813c669cb0ebc7b522 - JWT / internal security token: three base64 parts separated by dots,
e.g.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYmYiOjE3ODM5NjM1NDd9.Lyr6fqc...
A JWT passed as Authorization: token <JWT> or Authorization: Bearer <JWT> returns 401 Unauthorized on both old and new Gitea instances.
The nbf (Not Before) claim in the JWT payload identifies it as a
security token, not an access token.
If a user provides a token that looks like a JWT (dots separating
base64 segments), it is NOT a Gitea API token. Direct them to
Settings → Applications → Generate New Token in the Gitea web UI, or
generate one via gitea admin user generate-access-token (see above).
K8s Gitea Reachability from Outside K8s
| Path | Works? | Notes |
|---|---|---|
http://10.0.30.203:3000 |
❌ Timeout | Port 3000 not exposed on VIP |
http://10.0.30.203 (Host: git.schoen.codes) |
✅ 200 | Traefik routes by Host header |
https://10.0.30.203 (Host: git.schoen.codes) |
✅ 200 | TLS via Traefik |
10.0.30.202:2222 (SSH) |
Separate LB | Gitea SSH LoadBalancer service |
git.schoen.codes (via /etc/hosts) |
✅ | Resolves to 10.0.30.203 |
Hermes Host Has No kubectl
The Hermes host (/home/debian) does not have kubectl, helm, or
kubeconfig. All K8s operations go through SSH to mgmt-runner (VM200)
at 10.0.30.124. See SKILL.md Section 1.2.
ArgoCD Repo Secret Also Needs Token Update
After generating a new Gitea API token, the ArgoCD repository secret must also be patched with the new token — otherwise ArgoCD's repo connection breaks even though the Hermes host git remote works:
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.30.124 \
"KUBECONFIG=/root/.kube/config kubectl patch secret \
argocd-repo-k8s-gitea -n argocd --type merge \
-p '{\"data\":{\"password\":\"$(echo -n TOKEN | base64)\"}}'"
Gitea Helm Chart Admin Password Extraction
The Gitea Helm Chart v12 auto-generates an admin password on first
install (unless explicitly set in values). The password is stored as
the GITEA_ADMIN_PASSWORD environment variable in the configure-gitea
init container — NOT in any K8s Secret or Helm values file.
# Extract admin password from the init container env vars:
ssh -i ~/.ssh/id_ed25519_proxmox root@10.0.30.124 \
"KUBECONFIG=/root/.kube/config kubectl get deploy gitea -n gitea \
-o jsonpath='{.spec.template.spec.initContainers[?(@.name==\"configure-gitea\")].env}'" \
| python3 -c "
import json,sys
for e in json.load(sys.stdin):
if e.get('name')=='GITEA_ADMIN_PASSWORD':
print(e['value'])
"
Additional env vars in the same container:
GITEA_ADMIN_USERNAME: the admin username (e.g.dominik)GITEA_ADMIN_PASSWORD_MODE:keepUpdated(chart syncs password on every sync) orinitialOnlyRequireReset
Pitfall: The password is NOT in the gitea-security K8s Secret
(that contains JWT-based internal tokens only — see "JWT ≠ Gitea API
Token" above). It is also NOT in the Helm values gitea.admin section
(unless explicitly set — by default the chart generates a random one).
Storing Credentials in 1Password
After extracting the admin password and generating an API token, store both in 1Password vault "Hermes":
- API Token: Category
API Credential, titleK8s Gitea Token (hermes-gitops). Requires two-step create+edit (see 1password-cli skill —op item createdoes not populate thecredentialfield for API Credential items). - Admin Login: Category
Login, titleK8s Gitea Admin (dominik). Works in one step withop item create --category="Login".
Steps Performed (Completed)
- Checked current remote:
git remote -v→ CT108 (10.0.30.105:3000) - Verified K8s Gitea reachable via Traefik VIP:
curl -H "Host: git.schoen.codes" http://10.0.30.203/→ 200 - Added
/etc/hostsentry:10.0.30.203 git.schoen.codes - Changed git remote URL to
http://dominik:TOKEN@git.schoen.codes/dominik/iac-homelab.git git fetch origin→Authentication failed(old CT108 token invalid on K8s Gitea)- User provided a JWT (internal security token from 1Password) — identified as NOT an API token (see pitfall above)
- Generated new API token via mgmt-runner SSH:
gitea admin user generate-access-token -u dominik -t hermes-gitops --scopes all --raw→4a3fd... - Updated git remote with new token:
git remote set-url origin "http://dominik:4a3fd...@git.schoen.codes/..." git fetch origin→ ✅ success (forced update fromd9135ca→68adfab)git push --dry-run origin main→ ✅ "Everything up-to-date"- Patched ArgoCD repo secret
argocd-repo-k8s-giteawith new token
Result
Hermes host git operations now use K8s Gitea exclusively. ArgoCD also pulls from K8s Gitea. The entire GitOps loop is self-contained in K8s. CT108 can be decommissioned (Task 10 — pending user approval).