Phase 2: deploy Gitea warm standby on the VPS

Pull-mirrors home GitLab's Homelabv4 repo on Gitea's own built-in mirror
scheduler (6h interval) - no custom sync job needed, per the original
plan. SQLite instead of the chart's default HA Postgres + Valkey cluster
(single-instance standby holding one small repo, not worth the extra
moving parts). A PostSync Job creates the mirror once, idempotently;
Gitea's scheduler handles all ongoing pulls after that.

Also added a public Cloudflare CNAME for gitlab.kube.huskypup.net ->
home.kube.huskypup.net: the VPS has no route to home's LAN via Netbird
(none of the mesh peers advertise that subnet, confirmed live), so
GitLab needs to be reachable the same way any other internet client
reaches it - home's public IP already has port 443 forwarded to
istio-ingressgateway from earlier this session.

Admin and GitLab-mirror credentials are plain Secrets created directly
on the VPS cluster (kubectl, not git) - same pattern as vault-unseal-key.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Scooby Husky
2026-08-17 22:05:24 -05:00
co-authored by Claude Sonnet 5
parent d6e8c2aa2d
commit 777edf3895
3 changed files with 204 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: vps-gitea
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "2" # same wave as vps-vault, both just need vps-minio
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: vps-standby
sources:
- repoURL: https://dl.gitea.com/charts/
chart: gitea
targetRevision: 12.7.0
helm:
valueFiles:
- $values/infrastructure/vps-standby/gitea/values.yaml
- repoURL: https://gitlab.kube.huskypup.net/Scooby/Homelabv4.git
targetRevision: main
ref: values
- repoURL: https://gitlab.kube.huskypup.net/Scooby/Homelabv4.git
targetRevision: main
path: infrastructure/vps-standby/gitea/manifests
destination:
name: vps-standby
namespace: gitea
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
@@ -0,0 +1,104 @@
---
# One-time setup of a pull-mirror repo in Gitea, pointing at the home
# GitLab GitOps repo. Gitea's own built-in mirror scheduler handles the
# ongoing periodic pulls after this (configured via gitea.config.mirror
# in values.yaml, DEFAULT_INTERVAL: 6h) - this job just creates the
# mirrored repo once, idempotently (skips if it already exists).
#
# GitLab credentials for the mirror source are a plain Secret created
# directly on this cluster (kubectl, not git):
# kubectl -n gitea create secret generic gitlab-mirror-credentials \
# --from-literal=username=<gitlab user> --from-literal=password=<PAT>
apiVersion: batch/v1
kind: Job
metadata:
name: gitea-setup-mirror
namespace: gitea
annotations:
argocd.argoproj.io/hook: PostSync
argocd.argoproj.io/hook-delete-policy: BeforeHookCreation
spec:
backoffLimit: 3
template:
spec:
restartPolicy: Never
securityContext:
runAsNonRoot: true
runAsUser: 65534
seccompProfile:
type: RuntimeDefault
containers:
- name: setup-mirror
image: curlimages/curl:8.10.1
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
command:
- /bin/sh
- -c
- |
set -eu
# Service name follows the Helm release name (vps-gitea).
GITEA_URL="http://vps-gitea-http.gitea.svc.cluster.local:3000"
REPO_OWNER="gitea_admin"
REPO_NAME="Homelabv4"
echo "==> Waiting for Gitea API..."
for i in $(seq 1 30); do
if curl -sf "${GITEA_URL}/api/v1/version" >/dev/null 2>&1; then
break
fi
sleep 5
done
echo "==> Checking if mirror repo already exists..."
EXISTS=$(curl -s -o /dev/null -w '%{http_code}' \
-u "${GITEA_ADMIN_USER}:${GITEA_ADMIN_PASS}" \
"${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}")
if [ "$EXISTS" = "200" ]; then
echo "==> Mirror repo already exists, nothing to do."
exit 0
fi
echo "==> Creating pull-mirror repo from GitLab..."
curl -sf -X POST \
-u "${GITEA_ADMIN_USER}:${GITEA_ADMIN_PASS}" \
-H "Content-Type: application/json" \
"${GITEA_URL}/api/v1/repos/migrate" \
-d "{
\"clone_addr\": \"https://gitlab.kube.huskypup.net/Scooby/Homelabv4.git\",
\"repo_name\": \"${REPO_NAME}\",
\"repo_owner\": \"${REPO_OWNER}\",
\"mirror\": true,
\"mirror_interval\": \"6h\",
\"private\": false,
\"auth_username\": \"${GITLAB_USER}\",
\"auth_password\": \"${GITLAB_TOKEN}\",
\"service\": \"gitlab\"
}"
echo "==> Done."
env:
- name: GITEA_ADMIN_USER
valueFrom:
secretKeyRef:
name: gitea-admin-secret
key: username
- name: GITEA_ADMIN_PASS
valueFrom:
secretKeyRef:
name: gitea-admin-secret
key: password
- name: GITLAB_USER
valueFrom:
secretKeyRef:
name: gitlab-mirror-credentials
key: username
- name: GITLAB_TOKEN
valueFrom:
secretKeyRef:
name: gitlab-mirror-credentials
key: password
@@ -0,0 +1,67 @@
# Gitea warm standby on the VPS - Phase 2. Pull-mirrors the home GitLab
# repo on a schedule (Gitea's built-in mirror feature - see
# manifests/setup-mirror-job.yaml for the one-time mirror setup), rather
# than a custom sync job.
#
# SQLite instead of the chart's default postgresql-ha + valkey-cluster:
# this is a single-instance standby holding one small mirrored repo, not
# a production multi-user Gitea - a full HA Postgres cluster + Valkey
# cluster would be a lot of extra moving parts (and VPS resources) for no
# real benefit here.
postgresql-ha:
enabled: false
postgresql:
enabled: false
valkey-cluster:
enabled: false
valkey:
enabled: false
persistence:
enabled: true
size: 10Gi
storageClass: local-path
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 512Mi
gitea:
admin:
existingSecret: gitea-admin-secret
passwordMode: keepUpdated
config:
database:
DB_TYPE: sqlite3
cache:
ADAPTER: memory
session:
PROVIDER: memory
queue:
TYPE: level
server:
DOMAIN: gitea.kube.huskypup.net
ROOT_URL: "https://gitea.kube.huskypup.net/"
SSH_PORT: 22
SSH_LISTEN_PORT: 2222
mirror:
ENABLED: true
DEFAULT_INTERVAL: 6h
metrics:
enabled: false
# No ingress - reached over Netbird from home while it's up, or directly
# on the VPS during an actual promoted-standby incident (same pattern as
# the other vps-standby services).
service:
http:
type: ClusterIP
port: 3000
ssh:
type: ClusterIP
port: 22