bcs-website/docs/deployment.md
Brad Rodgers fc64909f3d
All checks were successful
CI / ci (push) Successful in 1m56s
docs: reconcile to as-built (ADR-0008, deploy runbook); clean text hero on detail pages
- 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)
2026-07-20 08:27:35 -04:00

4.0 KiB

Deployment

Local Commands

npm install
npm run dev
npm run typecheck
npm run build
npm run preview

Configuration

Set these at build time:

PUBLIC_SITE_URL=https://www.bluecapstrategies.com
PUBLIC_CONTACT_FORM_ENDPOINT=https://your-form-endpoint.example

PUBLIC_CONTACT_FORM_ENDPOINT can point to Formspree, a webhook, ntfy, or a self-hosted form receiver. Without it, the contact form validates fields and opens an email draft to info@bluecapstrategies.com.

Container

docker build -t bluecap-strategies-website:latest .
docker run --rm -p 8080:80 bluecap-strategies-website:latest

k3s (as-built — deployed & serving in-cluster since 2026-07-17)

Live GitOps flow (all working; see docs/gitops-deployment-strategy.md + ADR-0001/0007/0008):

local edit -> git push (Gitea main) -> Gitea Actions: build + seo:lint -> push image (internal
  10.66.15.22:3000) -> bump DEPLOY_IMAGE tag + commit back [skip ci] -> ArgoCD reconciles k3s ->
  k3s pulls git.7tl-homelab.com/... (private, ES pull secret, public HTTPS) -> pods serve
  • ArgoCD is the reconciler (not Flux); the CI runner never gets kubeconfig or cluster access.
  • Not yet public — the ingress names bluecapstrategies.com/www but DNS still points at the old WordPress site. Going public = the DNS Cutover below, a deliberate step. See the keep-old-site-live rule: do nothing that affects the old site until cutover.

Operate

# Review the running site locally (no cluster/DNS impact):
kubectl -n bluecap-strategies port-forward svc/bluecap-website 18080:80   # then http://localhost:18080
# or purely off-cluster from source:  npm run dev

# Health / status:
kubectl -n argocd get application bcs-website                 # Synced / Healthy
kubectl -n bluecap-strategies get pods,deploy                 # 2/2 Running
kubectl -n bluecap-strategies get externalsecret gitea-registry   # SecretSynced

# Rollback: `git revert` the tag-bump commit and push (ArgoCD reconciles), or use ArgoCD history.

Troubleshoot (things that actually bit us)

  • CI job never runs / "no matching runner" → runner offline or mis-scoped. Must be a global (owner_id=0) runner on gitea_network with instance URL http://gitea:3000; token must be an instance-level registration token (Site Admin → Actions → Runners).
  • Push: "server gave HTTP response to HTTPS client" → runner host dockerd missing insecure-registries: ["10.66.15.22:3000"] (gitea Ansible role handles it).
  • Push: unauthorizedCI_TOKEN repo secret missing/insufficient (write:package + write:repository).
  • Pod ImagePullBackOff → ExternalSecret not synced (check OpenBao cluster/bluecap-registry fields) or the read:package PAT.
  • git push rejected (fetch first) → CI committed a tag bump to main; git pull --rebase first (git config pull.rebase true).

Manual kubectl apply -k k8s/ is for emergency/bootstrap only, not normal deploys.

Ingress/TLS items to confirm before cutover: whether Pangolin/Newt terminates/passes/proxies TLS to Traefik and preserves Host headers; whether cert-manager's Cloudflare DNS-01 token can issue for bluecapstrategies.com; whether the canonical host is apex or www.

DNS Cutover

Before cutover:

  • Build and push the final container image.
  • Confirm the GitHub Actions workflow publishes the GHCR image and updates the desired image tag.
  • Confirm ArgoCD reconciles the bluecap-strategies workload from Git.
  • Confirm the ingress or edge route answers for www.bluecapstrategies.com and bluecapstrategies.com.
  • Lower DNS TTL on the existing records.
  • Point BlueCap DNS to the existing Pangolin/Newt public edge, or to the chosen production edge if that architecture changes.
  • Configure apex-to-canonical redirect at the selected layer: Cloudflare, Pangolin/Newt, Traefik, or nginx.
  • Verify /, /services/, /about-us/, /contact/, /sitemap-index.xml, and /robots.txt after DNS propagation.