Packaging & deployability: prod images, Helm, Terraform (AWS/GCP/Azure), CI/CD, ops#18
Merged
Merged
Conversation
Multi-stage, non-root images (backend/Dockerfile.prod, frontend/ Dockerfile.prod) and docker-compose.prod.yml (pgvector, redis, one-shot migrate, uvicorn backend, arq worker, nginx edge). Frontend SPA builds same-origin and nginx reverse-proxies /api + /mcp. Dev Dockerfiles/compose untouched. client.ts uses `??` so an empty VITE_API_URL is honored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
backend Deployment + HPA + PDB, arq worker, frontend + PDB, path-based ingress (/api + /mcp -> backend, / -> SPA), ServiceAccount. Alembic runs as a pre-install/pre-upgrade hook Job (ConfigMap/Secret hooks ordered before it) so migrations land before new pods roll and replicas never race. Secrets via chart-created Secret or existingSecret (external-secrets seam). Passes helm lint + kubeconform -strict. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each provisions managed Postgres 16 (pgvector) + managed Redis + a secret store with the assembled DSNs/keys (keys map 1:1 to backend env for external-secrets) + object storage + optional network + an identity/policy for external-secrets. Compute (EKS/GKE/AKS) is deliberately separate state. All three pass terraform validate + fmt; lockfiles committed, tfvars gitignored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
release.yml builds and pushes both images to GHCR, then deploys via a reusable helm-deploy composite action: push to main -> staging, tag v* -> production (gated by environment reviewers). Deploys pin to the commit SHA with --wait --atomic (auto-rollback). deploy-validate.yml lints the chart (kubeconform) and Terraform (fmt/validate) on PRs touching deploy/**. actionlint-clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
backup.sh (pg_dump custom format -> AES-256/openssl) and restore.sh (decrypt -> pg_restore, guarded by RESTORE_CONFIRM), an in-cluster backup CronJob example, a DR runbook (backup/restore, region rebuild, Alembic upgrade path, quarterly rotation with the ENCRYPTION_KEY caveat), and a production config reference. Scripts are shellcheck-clean. deploy/README.md ties the whole track together. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CHANGELOG entry under 2.0.0, a Production Deployment section + feature bullet in README, and the full Packaging & deployability section in CLAUDE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the Packaging & deployability parallel track from
planfull.md— the production path from commit to running cluster. All artifacts live underdeploy/(+ root prod compose /.github/); the devdocker-compose.ymlandDockerfiles are untouched.What's included
backend/Dockerfile.prod(builder venv → slim runtime, healthcheck) andfrontend/Dockerfile.prod(Vite build → unprivileged nginx serving the SPA and reverse-proxying/api+/mcp). Same-origin build viaVITE_API_URL=""(client.tsnow uses??).docker-compose.prod.yml) — pgvector, redis, one-shotmigrate(gated so replicas never race), uvicorn backend, arqworker, nginx edge. Configured by.env.prod(.env.prod.example).deploy/helm/querywise/) — backend Deployment + HPA + PDB, arq worker, frontend + PDB, path-based ingress, ServiceAccount, and an Alembicpre-install/pre-upgrademigration-hook Job. Secrets via chart Secret orexistingSecret(external-secrets seam).deploy/terraform/{aws,gcp,azure}/) — each provisions the data plane + secrets (managed Postgres 16/pgvector, managed Redis, a secret store with the assembled DSNs/keys, object storage, optional network, an identity/policy for external-secrets) in the customer's own account/VPC. Compute is intentionally separate state..github/) —release.ymlbuilds + pushes both images to GHCR then Helm-deploys (main→ staging, tagv*→ production,--wait --atomic) via a reusable composite action;deploy-validate.ymllints chart + Terraform on PRs.deploy/ops/) — encryptedbackup.sh/restore.sh, a backup CronJob example, a DR runbook, and a production config reference.Design notes
-10hooks, the migrate Job-5, so config exists first, migrations finish before new backend pods roll, and N replicas never race onalembic upgrade.dataFrominto thequerywise-secretsk8s Secret the chart references.ENCRYPTION_KEYrotation caveat is documented loudly in the runbook (it Fernet-encrypts stored connection strings — re-encrypt before swapping).Validation
All offline checks pass:
helm lint+helm template | kubeconform -strict(13/13),terraform validate+fmtacross all three clouds,actionlintclean,shellcheckclean, backup CronJob kubeconform-valid. Fullterraform plan/ live deploy need cloud + cluster credentials (deploy-time).Deferred
The managed-SaaS control plane (provisioning/billing/fleet upgrades) — additive, since each tenant is already an isolated instance.
🤖 Generated with Claude Code