Infrastructure
infra/terraform provisions both deployment tracks from one root module, with the expensive half
behind flags.
terraform apply -var project=<id> # serverless: Cloud Run + Batch
terraform apply -var project=<id> -var deploy_kubernetes=true \
-var deploy_cloud_sql=true # adds GKE, Argo, Cloud SQL
What the default track creates
| Resource | Why |
|---|---|
| Cloud Run services (api, web) | min-instances=0, so nothing runs when nobody is looking |
| Cloud Run job (nextflow driver) | exists only while a pipeline runs |
| Google Batch | VEP tasks on Spot VMs |
| GCS bucket | VCFs, the VEP cache, model artifacts |
| Secret Manager | the database URL, and any Nextflow secrets |
| Artifact Registry | images CI pushes |
| Service accounts + IAM | one per component, least privilege |
GKE Autopilot and Cloud SQL are opt-in because a Kubernetes control plane and a managed database are most of what a demonstration estate costs.
Cost as a design constraint
A portfolio platform is idle more than 99% of the time, so idle cost is the only cost that matters.
The reasoning, including why Google Cloud rather than AWS, is in docs/cloud.md. The short version:
GKE's free tier covers one control plane where EKS charges about $73/month, and Cloud Run's
scale-to-zero plus Batch on Spot puts the idle bill near £1/month — almost all of it the database.
The Kubernetes track is meant to be turned on, demonstrated, and destroyed:
make serverless-destroy PROJECT=<project id>
Authentication from CI
Workload Identity Federation. No service account key exists in the repository or in repository secrets. CI exchanges its OIDC token for short-lived credentials.
Configuration that matters
| Variable | Effect |
|---|---|
deploy_kubernetes |
GKE, Argo, the whole second track |
deploy_cloud_sql |
managed Postgres instead of an external URL |
database_url |
an external Postgres (a free tier, say) for the serverless track |
model_uri |
score from an artifact, with no MLflow server running |
Terraform is fmt -checked and validated on every pull request — see Testing and CI.
rarelens
Understanding it
Working on it
Running it
When it goes wrong