Skip to content

Deployment

Backend and web ship together as one version; the native mobile app versions separately. A merge to main auto-deploys backend + web to dev; a vX.Y.Z tag gates prod. Everything is GitOps — no human kubectl.

End-to-end flow

merge to main
↓ auto (no approval)
dev · automatic
Cloud Build (backend + web)
↓ image :sha → AR
Argo Image Updater → Argo CD
GKE tomoda-dev
prod · tag is the gate
tag v* (backend + web)
↓ auto
Cloud Build → AR → Argo
GKE tomoda-prod
mobile app
JS merge → OTA dev (Xavia)
↓ promote
OTA prod
↓ native change only
local build → TestFlight / Play
Three lanes. Backend/web deploy to dev on merge and to prod together on one v* tag. The mobile app ships JS over-the-air (dev channel on merge, prod on promote); native binaries are built locally only when native code changes.

Step by step:

  1. Merge to main → Cloud Build builds backend + web automatically (no approval) and pushes :sha images to Artifact Registry tomoda-dev-repo.
  2. Tag for prodvX.Y.Z ships backend + web together (see scripts/release.sh). The tag push triggers both prod Cloud Build jobs → tomoda-prod-repo. See Cloud Build and DevOps → Cloud Build.
  3. Argo CD Image Updater detects the new image and commits a Kustomize image override to the devops repo.
  4. Argo CD reconciles the manifests to the target namespace. See DevOps → Argo CD.
  5. Rolling update, gated by the readiness probe. Prod has a PodDisruptionBudget with minAvailable: 1.

Where the manifests live

The shape of the application in the cluster is defined entirely in the DevOps repo under k8s/apps/tomoda/:

  • base/ — Deployments (backend + frontend), Ingress (Traefik, path-routed /api, /ws → backend), Service, NetworkPolicy, Kustomization
  • overlays/dev/ — namespace tomoda, dev image repo
  • overlays/prod/ — namespace prod, prod image repo, PodDisruptionBudget

See DevOps → Tomoda K8s app for the manifests, resource limits, replicas, env vars, hostnames, probes, and the canonical health-check definition. Read those manifests, not this page, for current values.

Frontend deploy

The frontend Pod (Nginx serving the static Expo web export) is deployed via the same flow. Because EXPO_PUBLIC_* env vars are baked in at build time, any change to public API URLs / OAuth client IDs requires a rebuild, not a config change — see Cloud Build.

Rollback

Rolling back is a Git operation on the DevOps repo, not the application repo. See DevOps → Rollback for the procedure (image rollback via Argo CD UI, git revert, or — last resort — kubectl rollout undo).

Native is different

The iOS and Android binaries are not deployed through this pipeline. They're built locally and submitted to TestFlight / Play by hand — and only when native code changes. Most changes reach installed apps over-the-air via the self-hosted Xavia OTA server (dev channel on merge, prod on promote). See Native → Release.