
Operating on CI/CD Platform
Companion operations guide for CI/CD Platform — Gitea, Tekton, Zot, and Cosign.
Quick Health Check
# All CI/CD pods
kubectl get pods -n gitea -o wide
kubectl get pods -n tekton-pipelines -o wide
kubectl get pods -n zot -o wide
# ArgoCD app status
kubectl get applications -n argocd gitea gitea-extras \
tekton-pipelines tekton-triggers tekton-dashboard tekton-extras \
zot zot-extras
# ExternalSecrets sync status
kubectl get externalsecret -n gitea
kubectl get externalsecret -n tekton-pipelines
kubectl get externalsecret -n zotHealthy state: all pods Running on pc-1, all ArgoCD apps Synced/Healthy, all ExternalSecrets SecretSynced.
$ kubectl get pods -n gitea -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
gitea-6d7d457c49-t5hsj 1/1 Running 0 7h40m 10.244.7.57 pc-1 <none> <none>
$ kubectl get pods -n tekton-pipelines -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
el-gitea-listener-7d85fd8b75-8w9z6 1/1 Running 3 (7h41m ago) 7d7h 10.244.7.226 pc-1 <none> <none>
tekton-dashboard-774bff7cc-92cmn 1/1 Running 0 22d 10.244.8.175 mini-3 <none> <none>
tekton-events-controller-5cbc777ccd-7gvq7 1/1 Running 0 22d 10.244.8.144 mini-3 <none> <none>
tekton-pipelines-controller-7496c46798-kgphw 1/1 Running 0 7d10h 10.244.12.233 mini-1 <none> <none>
tekton-pipelines-webhook-75cd84877-tgctj 1/1 Running 0 22d 10.244.12.139 mini-1 <none> <none>
tekton-triggers-controller-66fd74568d-m8zsv 1/1 Running 0 22d 10.244.8.254 mini-3 <none> <none>
tekton-triggers-core-interceptors-66456f8cf6-blt4s 1/1 Running 0 22d 10.244.8.181 mini-3 <none> <none>
tekton-triggers-webhook-55c8dd895f-j95ft 1/1 Running 0 22d 10.244.8.187 mini-3 <none> <none>
$ kubectl get pods -n zot -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
zot-68c79b95f9-6vbhh 1/1 Running 0 7h41m 10.244.7.202 pc-1 <none> <none>
Gitea Operations
Mirror Sync Status
# Check mirror sync time via API
GITEA_URL="http://192.168.55.209:3000"
curl -s "$GITEA_URL/api/v1/repos/tekton-bot/frank" | jq '{
mirror: .mirror,
updated_at: .updated_at,
mirror_interval: .mirror_interval
}'Expected: mirror: true, updated_at within the last 10 minutes.
Force Mirror Sync
# Trigger immediate mirror sync
ADMIN_TOKEN=$(kubectl get secret -n gitea gitea-secrets -o jsonpath='{.data.admin-password}' | base64 -d)
curl -sf -X POST "$GITEA_URL/api/v1/repos/tekton-bot/frank/mirror-sync" \
-H "Authorization: token $ADMIN_TOKEN"Gitea Logs
kubectl logs -n gitea deploy/gitea --tail=50
kubectl logs -n gitea deploy/gitea -f # FollowRestart Gitea
kubectl rollout restart deploy/gitea -n gitea
kubectl rollout status deploy/gitea -n giteaNote: Gitea uses strategy: Recreate (RWO PVC) — expect a brief downtime window during restart.
Common Gitea Issues
| Symptom | Cause | Fix |
|---|---|---|
| OIDC login fails | Authentik provider misconfigured or down | Check kubectl logs -n gitea deploy/gitea | grep oauth |
| Mirror not updating | GitHub PAT expired or rate-limited | Verify GITHUB_MIRROR_TOKEN in Infisical is valid |
| Webhook delivery fails | ALLOWED_HOST_LIST missing cluster DNS | Add *.svc.cluster.local to gitea.config.webhook.ALLOWED_HOST_LIST |
| Pod stuck in Pending | PVC can’t mount (pc-1 down) | Check kubectl get pv and node status |
Tekton Operations
List Recent PipelineRuns
# All PipelineRuns, most recent first
kubectl get pipelinerun -n tekton-pipelines --sort-by=.metadata.creationTimestamp
# Only failed runs
kubectl get pipelinerun -n tekton-pipelines \
-o jsonpath='{range .items[?(@.status.conditions[0].status=="False")]}{.metadata.name}{"\t"}{.status.conditions[0].message}{"\n"}{end}'View Pipeline Logs
# Latest PipelineRun logs (requires tkn CLI)
tkn pipelinerun logs -n tekton-pipelines --last
# Without tkn — find the pod and read logs per step
kubectl get pods -n tekton-pipelines -l tekton.dev/pipelineRun --sort-by=.metadata.creationTimestamp | tail -5
kubectl logs -n tekton-pipelines <pod-name> -c step-clone
kubectl logs -n tekton-pipelines <pod-name> -c step-test
kubectl logs -n tekton-pipelines <pod-name> -c step-build-and-push
kubectl logs -n tekton-pipelines <pod-name> -c step-signCancel a Running PipelineRun
kubectl patch pipelinerun -n tekton-pipelines <name> \
--type=merge -p '{"spec":{"status":"CancelledRunFinally"}}'Clean Up Old PipelineRuns
# Delete PipelineRuns older than 7 days
kubectl get pipelinerun -n tekton-pipelines \
-o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.creationTimestamp}{"\n"}{end}' \
| awk -v cutoff="$(date -d '7 days ago' -u +%Y-%m-%dT%H:%M:%SZ)" '$2 < cutoff {print $1}' \
| xargs -r kubectl delete pipelinerun -n tekton-pipelinesEventListener Health
# EventListener pod logs
kubectl logs -n tekton-pipelines -l app.kubernetes.io/managed-by=EventListener --tail=30
# EventListener service reachability (from within cluster)
kubectl run test-curl --rm -it --image=curlimages/curl -- \
curl -s -o /dev/null -w "%{http_code}" \
http://el-gitea-listener.tekton-pipelines.svc.cluster.local:8080
# Expect: 200 or 202Tekton Dashboard
Access at http://192.168.55.217:9097 or https://tekton.cluster.derio.net (Authentik forward-auth).
The dashboard is read-only — it shows PipelineRuns, TaskRuns, and logs. Useful for non-CLI users or quick visual debugging.
Common Tekton Issues
| Symptom | Cause | Fix |
|---|---|---|
| PipelineRun stuck in Pending | PVC can’t provision (pc-1 down or Longhorn unhealthy) | Check kubectl get pv and Longhorn UI |
ComparisonError in ArgoCD | Task YAML uses resources instead of computeResources | Fix the Task manifest and re-sync |
report-success not firing | $(tasks.status) is "Completed" not "Succeeded" | Check when clause accepts both values |
Step fails with permission denied | Missing fsGroup: 65534 on PipelineRun pod template | Add to TriggerTemplate’s taskRunTemplate.podTemplate.securityContext |
git config fails | HOME=/ is read-only for UID 65534 | Set HOME=/tekton/home env var on the step |
| PodSecurity violation | Missing securityContext on Task steps | Add runAsNonRoot, capabilities.drop, seccompProfile |
| Webhook doesn’t trigger pipeline | Gitea sends X-Gitea-Event, not X-GitHub-Event | Use CEL interceptor, not github interceptor |
Zot Registry Operations
Registry Health
# API check (anonymous read)
curl -sk https://192.168.55.210:5000/v2/
# Expect: {} or {"repositories":[]}
# List all repositories
curl -sk https://192.168.55.210:5000/v2/_catalog
# Expect: {"repositories":["test/alpine","..."]}
# List tags for a repo
curl -sk https://192.168.55.210:5000/v2/test/alpine/tags/listTest Push
# Push a test image (requires credentials)
ZOT_PASS=$(kubectl get secret -n tekton-pipelines zot-push-creds \
-o jsonpath='{.data.\.dockerconfigjson}' | base64 -d | jq -r '.auths["192.168.55.210:5000"].password')
# Using crane (lightweight OCI tool)
crane auth login 192.168.55.210:5000 -u tekton-push -p "$ZOT_PASS" --insecure
crane push /dev/null 192.168.55.210:5000/test/healthcheck:latest --insecureTLS Certificate Status
# cert-manager Certificate status
kubectl get certificate -n zot zot-tls
# Expect: Ready=True
# Certificate details
kubectl describe certificate -n zot zot-tls
# Check cert expiry
kubectl get secret -n zot zot-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -datesRegistry Logs
kubectl logs -n zot deploy/zot --tail=50Restart Zot
kubectl rollout restart deploy/zot -n zot
kubectl rollout status deploy/zot -n zotLike Gitea, Zot uses strategy: Recreate for RWO PVC safety.
Common Zot Issues
| Symptom | Cause | Fix |
|---|---|---|
401 Unauthorized on push | Wrong password or htpasswd hash stale | Regenerate htpasswd if ZOT_PUSH_PASSWORD changed in Infisical |
x509: certificate signed by unknown authority | Self-signed cert not trusted by client | Use --insecure flag or add cert to trust store |
| Containerd pull fails on nodes | Talos mirror patch not applied | Apply patches/phase06-cicd/06-cluster-zot-registry.yaml via Omni |
| Pod stuck (PVC) | pc-1 down or Longhorn unhealthy | Check node and Longhorn status |
Cosign Operations
Verify an Image Signature
cosign verify --key apps/tekton/cosign.pub \
--insecure-ignore-tlog --allow-insecure-registry \
192.168.55.210:5000/<repo>/<image>:<tag>Expected: Verification for 192.168.55.210:5000/... -- The following checks were performed: ...
Check if an Image Is Signed
# List signature artifacts for an image
crane ls 192.168.55.210:5000/<repo>/<image> --insecure
# Look for sha256-*.sig tagsRotate Signing Keys
- Generate new key pair:
COSIGN_PASSWORD='' cosign generate-key-pair - Store new private key in Infisical as
COSIGN_KEY - Commit new
cosign.pubtoapps/tekton/cosign.pub - Wait for ExternalSecret to refresh (5 minutes) or force sync
- New images will be signed with the new key; old signatures remain valid with old public key
End-to-End Webhook Test
To verify the full pipeline chain from Gitea to signed image:
# 1. Trigger a mirror sync (or push directly to Gitea)
curl -sf -X POST "http://192.168.55.209:3000/api/v1/repos/tekton-bot/frank/mirror-sync" \
-H "Authorization: token $ADMIN_TOKEN"
# 2. Watch for new PipelineRun
kubectl get pipelinerun -n tekton-pipelines -w
# 3. Check Gitea commit status (after pipeline completes)
curl -s "http://192.168.55.209:3000/api/v1/repos/tekton-bot/frank/statuses/<SHA>" \
-H "Authorization: token $TOKEN" | jq '.[0].state'
# Expect: "success"