Skip to content

Deployment

A backend or frontend change reaches production through a tag → CI → ArgoCD → GKE pipeline. There is no kubectl apply step run by a human; everything is GitOps.

End-to-end flow

flowchart TD
    Dev[Developer] -->|scripts/release.sh| Tag[git tag v1.2.3]
    Tag -->|push| GitHub[GitHub: tomoda repo]

    GitHub -->|tag trigger| CB1[Cloud Build: backend]
    GitHub -->|tag trigger| CB2[Cloud Build: frontend]

    CB1 --> AR[(Artifact Registry)]
    CB2 --> AR

    AR -.image updater.-> DevopsRepo[GitHub: devops repo<br/>K8s manifests + Kustomize]
    DevopsRepo --> ArgoCD[ArgoCD]
    ArgoCD -->|reconcile| GKE[GKE cluster]

Step by step:

  1. Tag the release. Run scripts/release.sh from the main branch. It bumps the VERSION files, commits, tags vX.Y.Z, and pushes both the commit and the tag.
  2. Cloud Build builds images. The tag push triggers two Cloud Build jobs (one backend, one frontend). Each produces an image and pushes tags to Artifact Registry (tomoda-prod-repo for tags, tomoda-dev-repo for main-branch builds with manual approval). See Cloud Build and DevOps → Cloud Build.
  3. Argo CD Image Updater detects the new image SHA in Artifact Registry and commits a Kustomize image override back to the devops repo.
  4. Argo CD reconciles. Argo CD watches the devops/ repo and applies the new manifests to the target K8s namespace using Kustomize. See DevOps → Argo CD.
  5. Rolling update. Kubernetes performs a rolling update of the Deployment, gated by the readiness probe. Prod has a PodDisruptionBudget with minAvailable: 1.

Where the manifests live

The shape of the application in GKE:

File Purpose
k8s/apps/tomoda/base/backend-deployment.yaml (in devops repo) Backend Deployment + ClusterIP Service
k8s/apps/tomoda/base/frontend-deployment.yaml Frontend (Nginx) Deployment + Service
k8s/apps/tomoda/base/ingress.yaml Traefik Ingress (path-routed: /api, /ws → backend; rest → frontend)
k8s/apps/tomoda/base/network-policy.yaml NetworkPolicy restricting pod-to-pod traffic
k8s/apps/tomoda/base/kustomization.yaml Kustomize base
k8s/apps/tomoda/overlays/dev/ Dev-environment overrides (namespace tomoda, dev image repo)
k8s/apps/tomoda/overlays/prod/ Prod-environment overrides (namespace prod, prod image repo, PDB)

See DevOps → Tomoda K8s app for the full breakdown.

The image reference in base/backend-deployment.yaml currently points at asia-east1-docker.pkg.dev/development-485000/tomoda-dev-repo/tomoda-backend:latest with imagePullPolicy: Always. Overlays may pin to a specific SHA.

Source of truth

Always read the manifests in devops/ for current resource limits, replicas, env vars, and host names. Anything quoted here is illustrative.

Health & readiness

The backend Deployment defines both a liveness and a readiness probe against GET /health on :8080:

livenessProbe:
  httpGet: { path: /health, port: 8080 }
  initialDelaySeconds: 15
  periodSeconds: 30
readinessProbe:
  httpGet: { path: /health, port: 8080 }
  initialDelaySeconds: 10
  periodSeconds: 10

/health is implemented in the backend (see backend/internal/handlers/health.go). A rolling update will not shift traffic to a new pod until the readiness probe passes.

Resources

The backend currently runs at modest limits (verify in devops/k8s/apps/tomoda/base/backend-deployment.yaml):

  • requests: 64Mi memory, 10m CPU
  • limits: 512Mi memory, 500m CPU
  • replicas: 1 (single-process WebSocket Hub — see Overview)

Rollback

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

Frontend deploy

The frontend Pod (Nginx serving the static Expo web export) is deployed via the same flow. Because the 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.

Mobile is different

The iOS and Android binaries are not deployed through this pipeline. They're built and submitted via EAS — see Native Release.