- ADR-0008: private package, External-Secrets pull over public HTTPS (no k3s restart); CI auth via CI_TOKEN PAT; Playwright dropped from CI. Marks ADR-0007 partially superseded. - gitops-deployment-strategy + deployment.md updated to as-built flow + operate/troubleshoot runbook - content-deviations: detail-page heroes are text-only (no image) - service/focus detail pages: clean text hero (drop shared photo/icon)
10 KiB
BlueCap Strategies GitOps Deployment Strategy (ArgoCD)
Goal
Deploy the BlueCap Strategies Astro site to the existing k3s cluster using GitOps, without giving Gitea Actions direct access to the homelab cluster.
The homelab already runs ArgoCD in k3s, so this strategy uses ArgoCD as the reconciler rather than standing up a second GitOps controller (Flux). ArgoCD pulls from Git and the image registry; the cluster is never exposed to the CI system.
Desired operating model:
- Edit the site locally in this repository.
- Commit changes.
- Push to
main. - Gitea Actions runs tests and builds a container image.
- Gitea Actions pushes the image to Gitea Packages, tagged with the immutable
<git-sha>. - Git is updated with the new desired image tag (CI commits the tag into
k8s/). - ArgoCD, running inside k3s, detects the Git change.
- ArgoCD syncs the Kubernetes manifests (Kustomize).
- k3s pulls the new image and rolls the website deployment.
- Traefik serves the updated site publicly.
The CI runner never receives kubeconfig or direct network access to the homelab cluster. The cluster pulls from Git and the image registry.
Concrete implementation is Gitea, not GitHub/GHCR — see ADR-0007. CI = Gitea Actions (
.gitea/workflows/ci.yml); registry = Gitea Packages (10.66.15.22:3000/bmr_bluecap/bcs-website); ArgoCD Application inargocd/bcs-website-application.yaml. Any lingering GitHub/GHCR wording below is the generic model; substitute Gitea/Gitea-Packages.
Prerequisites & as-built registry flow
As built (2026-07-17) — see ADR-0008. The earlier "k3s trusts the HTTP registry + restart" plan was dropped. Final flow avoids any k3s restart or per-node config.
Registry flow (split push/pull, same private Gitea package, two access paths):
- CI pushes to the internal
10.66.15.22:3000/bmr_bluecap/bcs-website(HTTP, fast). The runner host's dockerd trusts it viainsecure-registries(gitea Ansible role + SIGHUP live-reload — no restart). Docker in jobs works because act_runner's config pins jobs togitea_networkand exposes the docker socket. - k3s pulls the public HTTPS
git.7tl-homelab.com/bmr_bluecap/bcs-website(valid TLS) → noregistries.yaml, no node config, no k3s restart. (k3s_registry_mirrorsstays empty.) - Pull auth: package is private; a
read:packagePAT lives in OpenBao (cluster/bluecap-registry) and External Secrets (k8s/externalsecret-registry.yaml) materializes thegitea-registrydockerconfigjson used by the Deployment'simagePullSecrets. - CI auth: dedicated PAT
CI_TOKEN(repo Actions secret;write:package+write:repository).
One-time setup (all done): repo on Gitea; runner registered (global/instance, on gitea_network,
correct instance URL); CI_TOKEN secret; OpenBao cluster/bluecap-registry; ArgoCD Application
applied. Deploys are now push-to-main → CI → ArgoCD, hands-off.
Cluster Findings
Read-only kubectl inspection previously showed:
- k3s API reachable at
10.66.15.30:6443. - Ingress controller:
traefik. Ingress class:traefik. - cert-manager installed with a
letsencrypt-prodClusterIssuer. - Existing namespaces for media, nextcloud, monitoring, etc.
- No
bluecap-strategiesnamespace yet. - ArgoCD is already installed and managing other workloads in the cluster.
Edge / DNS / TLS (research 2026-06-05):
- Edge routing uses a Pangolin/Newt path (
dav.rodgersweb.comvia Caddy). bluecapstrategies.comDNS is managed by Cloudflare.- TLS handled by cert-manager via a Cloudflare DNS-01 token.
Repository & Image Strategy
- Mono-repo: Keep site source and k8s manifests in the same repository.
- Gitea Packages: Use Gitea's built-in container registry (
10.66.15.22:3000) for images. - Immutable tags: Deploy via
<git-sha>tags, neverlatest.latestdefeats ArgoCD's ability to detect and record what is actually running. - Manifests: Plain Kustomize under
k8s/(already present). ArgoCD consumesk8s/kustomization.yamlnatively — no Helm chart required.
Phase 1: ArgoCD Application (Foundational GitOps)
Register the site as an ArgoCD Application. Two common patterns; pick one to match how the
homelab already organizes ArgoCD:
- Direct Application — a single
ApplicationCR pointing at this repo'sk8s/path. - App-of-apps — add this
Applicationto the existing root/app-of-apps repo if the homelab uses that pattern.
Example direct Application (store in the homelab ArgoCD config repo, or apply once to bootstrap):
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: bluecap-website
namespace: argocd
spec:
project: default
source:
repoURL: http://10.66.15.22:3000/bmr_bluecap/bcs-website.git
targetRevision: main
path: k8s
destination:
server: https://kubernetes.default.svc
namespace: bluecap-strategies
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
prune: true removes resources deleted from Git; selfHeal: true reverts manual drift;
CreateNamespace=true lets ArgoCD create bluecap-strategies (or keep namespace.yaml in
k8s/ — both work, don't do both destructively).
Image Update Strategy
Two options — this strategy recommends Option A for simplicity and auditability:
Option A (recommended): CI commits the image tag
Gitea Actions, after pushing the image, patches the image tag in k8s/ (via
kustomize edit set image or a newTag field) and commits back to main. ArgoCD auto-syncs the
new tag. Every deploy is a Git commit traceable to a source SHA — clean rollback via git revert.
Add to k8s/kustomization.yaml:
images:
- name: 10.66.15.22:3000/bmr_bluecap/bcs-website
newTag: <ci-sets-this>
Option B: ArgoCD Image Updater
Install ArgoCD Image Updater and annotate the Application to watch the Gitea Packages repo and write back the tag. Removes the CI commit-back step but adds a controller and registry-credential wiring. Only adopt if the homelab already runs Image Updater for other apps.
Phase 2: CI Pipeline (Gitea Actions)
Workflow on push to main:
- Quality gate:
npm ci,npm run typecheck,npm run build. - E2E validation:
npm run test:e2e(Playwright) where browser deps are available. - Publish: build the Docker image, push to Gitea Packages tagged
<git-sha>. - GitOps sync (Option A):
kustomize edit set image ...=10.66.15.22:3000/bmr_bluecap/bcs-website:<git-sha>, commit and push tomain. ArgoCD reconciles.
CI never touches the cluster — it only writes to Gitea Packages and Git.
Phase 3: Staging (Launch Readiness)
Avoid testing in production. Add a k8s/overlays/staging/ Kustomize overlay and a second ArgoCD
Application tracking it.
- Target:
staging.bluecapstrategies.com - Goal: let the owner review content/insights changes before they hit the live site.
Phase 4: Elite Authority Infrastructure (9/10 Standard)
1. Secret hygiene — Git-managed credentials
Avoid manual kubectl create secret. Use Sealed Secrets or External Secrets Operator so
Gitea Packages pull tokens and contact-form keys are encrypted in Git and the site is reproducible from
source alone. Match whichever the homelab ArgoCD stack already standardizes on.
2. Edge authority — Cloudflare hardening
- WAF & protection: enable Cloudflare WAF to block automated probes and DDoS.
- Edge caching: "Cache Everything" for static Astro assets so most traffic serves from Cloudflare's edge regardless of homelab bandwidth.
- Cloudflare Tunnels (optional): consider
cloudflaredto remove open router ports.
3. Uptime intelligence (observability) — reuse the monylog stack
The homelab already runs a full LGTM + Alertmanager stack on the monylog box (10.66.15.21,
Ansible role pglta), external to k8s. Do not deploy Uptime Kuma. Alloy already remote-writes
BlueCap pod/deployment metrics (kube-state-metrics) and ships nginx pod logs to Loki. To finish:
- Add
blackbox_exporterto the monylog compose + a Prometheus scrape job probing the in-cluster Service and the public URL (uptime, HTTP status, TLS-expiry, latency). - Add a
web-bluecap.ymlalert rules file (SiteDown, CertExpiringSoon <14d, High5xx, DeploymentDegraded, PodRestarting) mirroring the existing host-infra rules. - Alerting already flows to Pushover (OpenBao creds) with ntfy fallback → phone; add a
service: bluecaplabel. Grafana already present for dashboards. ArgoCD sync/health is an extra deploy-level signal. Seedocs/seo-aeo-optimization.md§6a for detail.
4. Digital ESG — verified sustainability
- Verify Green Web status if the homelab uses renewable energy.
- Use Astro's
Imagecomponent to minimize data transfer. - Document the low-energy architecture in
llms.txtor a footer "Digital ESG" note.
5. Security performance (A+ rating)
- Harden
deploy/nginx.conftoward an A+ onsecurityheaders.com: robust CSP, HSTS, X-Frame-Options. - TLS 1.3 only; cert-manager keeps certs well ahead of expiry.
Verification & Rollback
Rollback
GitOps makes rollbacks surgical:
git revert <commit-sha>git push origin main- ArgoCD reconciles the previous known-good state automatically. (Or use ArgoCD's History/Rollback UI to pin an earlier synced revision.)
Automated checks
npm run test:e2e(pre-deployment, in CI)- K8s readiness/liveness probes on
/healthz(during rollout) - ArgoCD health status + Uptime Kuma (post-deployment)
Open Items Before Production Ingress
- Confirm whether Pangolin/Newt terminates, passes through, or proxies TLS to Traefik, and
whether it preserves
Hostheaders for the BlueCap hostnames. - Confirm cert-manager's Cloudflare DNS-01 token can issue for
bluecapstrategies.com. - Decide canonical host (apex vs
www) and where the apex→canonical redirect lives (Cloudflare, Pangolin/Newt, Traefik, or nginx).