fastpass — Cluster Bootstrap
This directory contains resources and documentation for the manual bootstrap
sequence required to bring up fastpass from a fresh Talos install.
Everything in this directory is applied once by hand before ArgoCD takes over. After Step 6 (app-of-apps), all further changes are made via Git.
Prerequisites
- All 6 Talos nodes
Ready(kubectl get nodes) - Cilium healthy (
cilium status --wait) - IP pools applied (
kubectl get ciliumloadbalancerippool) city-hallhashelm,kubectl, andtalosctlinstalled- SSH keypair for ArgoCD → Gitea exists at
/home/wed/.ssh/argocd_gitea - 1Password credentials file at
/home/wed/1password-credentials.json - 1Password Connect token at
/home/wed/connect-token(no trailing newline)
Step 1 — Install Gateway API CRDs
The Gateway API CRDs are not bundled with Cilium — they must be installed independently before Cilium can register the GatewayClass controller.
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml
Verify:
kubectl get crd gateways.gateway.networking.k8s.io
Step 2 — Create the Cilium GatewayClass
The Cilium operator manages this object but does not auto-create it.
kubectl apply -f cluster/bootstrap/gatewayclass.yaml
Verify (may take a few seconds):
kubectl get gatewayclass
# Expected: cilium io.cilium/gateway-controller True
Step 3 — Create the argocd namespace
Must be created before Helm — the Gitea repo secret targets this namespace.
kubectl apply -f cluster/argocd/namespace.yaml
Step 4 — Create the Gitea repo secret
kubectl create secret generic gitea-repo \
--namespace argocd \
--from-literal=type=git \
--from-literal=url=git@gitea.mk-labs.cloud:rblundon/homelab.git \
--from-file=sshPrivateKey=/home/wed/.ssh/argocd_gitea
kubectl label secret gitea-repo -n argocd \
argocd.argoproj.io/secret-type=repository
Verify:
kubectl -n argocd get secret gitea-repo
Step 5 — Install ArgoCD via Helm
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
helm upgrade --install argocd argo/argo-cd \
--namespace argocd \
--create-namespace \
--version 7.8.23 \
--values cluster/argocd/values.yaml \
--wait
Wait for all ArgoCD pods to be ready:
kubectl -n argocd get pods --watch
Step 6 — Add Gitea to ArgoCD known hosts
ssh-keyscan -t ed25519 gitea.mk-labs.cloud 2>/dev/null | \
kubectl patch configmap argocd-ssh-known-hosts-cm -n argocd \
--type merge \
-p "{\"data\":{\"ssh_known_hosts\": \"$(ssh-keyscan -t ed25519 gitea.mk-labs.cloud 2>/dev/null)\"}}"
Step 7 — Get the initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath='{.data.password}' | base64 -d; echo
Access the UI via port-forward (ingress not yet deployed):
kubectl port-forward svc/argocd-server -n argocd 8080:80 > /tmp/pf.log 2>&1 &
Browse to http://localhost:8080
Step 8 — Apply the app-of-apps
This is the final manual kubectl apply. It hands control to ArgoCD:
kubectl apply -n argocd -f cluster/argocd/apps-of-apps.yaml
From this point, all changes are made via Git. ArgoCD syncs cluster/platform/
and cluster/applications/ automatically.
Watch the sync:
kubectl -n argocd get applications --watch
Step 9 — Approve CSRs
kubectl get csr --no-headers | awk '{print $1}' | xargs kubectl certificate approve
Run periodically until no pending CSRs remain.
Step 10 — Bootstrap 1Password Connect secrets
Once onepassword-connect namespace exists (created by ArgoCD sync):
# Verify namespace exists
kubectl get namespace onepassword-connect
# Credentials secret — must be base64-encoded
kubectl create secret generic op-credentials \
--namespace onepassword-connect \
--from-literal=1password-credentials.json=$(base64 -w 0 /home/wed/1password-credentials.json)
# Token secret — file must have no trailing newline
kubectl create secret generic connect-token \
--namespace onepassword-connect \
--from-file=token=/home/wed/connect-token
Restart Connect to pick up secrets:
kubectl -n onepassword-connect rollout restart deployment onepassword-connect
kubectl -n onepassword-connect rollout status deployment onepassword-connect
Verify (should show only GET /health and GET /heartbeat 200s):
kubectl -n onepassword-connect logs deployment/onepassword-connect -c connect-api 2>&1 | tail -10
Expected platform sync order
| Wave | App |
|---|---|
| 0 | apps-of-apps (root) |
| 1 | external-secrets |
| 1 | onepassword-connect |
| 2 | nfs-csi |
| 3 | cert-manager |
| 4 | ingress-nginx |
| 5 | external-dns |
Troubleshooting
GatewayClass not ACCEPTED after applying: Restart the Cilium operator — it may have started before the CRDs were present:
kubectl rollout restart deployment/cilium-operator -n kube-system
kubectl get gatewayclass
ArgoCD can't reach Gitea:
kubectl -n argocd logs deployment/argocd-repo-server | grep -i "error\|ssh"
Check that gitea-repo secret exists and known hosts configmap has gitea.mk-labs.cloud.
App stuck in Unknown state:
kubectl -n argocd get application <name> -o yaml | grep -A10 "conditions"
ArgoCD pruning itself:
The argocd namespace Applications must have prune: false. Never enable
prune on any Application that manages the argocd namespace.
Helm valueFiles field:
The correct field name is valueFiles (plural). valuesFile and valuesFiles
are silently ignored — ArgoCD syncs green but values aren't applied.
1Password Connect base64 error:
illegal base64 data at input byte 0 means credentials were stored as raw JSON.
Delete and recreate:
kubectl delete secret op-credentials -n onepassword-connect
kubectl create secret generic op-credentials \
--namespace onepassword-connect \
--from-literal=1password-credentials.json=$(base64 -w 0 /home/wed/1password-credentials.json)
kubectl -n onepassword-connect rollout restart deployment onepassword-connect
1Password Connect invalid Authorization header: Token has a trailing newline. Recreate:
echo -n "your-token" > /home/wed/connect-token
kubectl delete secret connect-token -n onepassword-connect
kubectl create secret generic connect-token \
--namespace onepassword-connect \
--from-file=token=/home/wed/connect-token
kubectl -n onepassword-connect rollout restart deployment onepassword-connect
Post-bootstrap
Once ingress-nginx and cert-manager are synced:
- ArgoCD UI: https://argocd.local.mk-labs.cloud
- Change admin password:
argocd account update-password - Authentik OIDC: uncomment
oidc.configincluster/argocd/values.yamlonce the ArgoCD provider is configured in Authentik - Migrate
gitea-reposecret to ESO once 1Password Connect is healthy