Skip to main content

· Documentation  · 3 min read

The template in depth — architecture, modules, and CI

A tour of how astro-gcp-cloudrun-starter is put together — the Terraform layers and modules, the keyless CI/CD pipeline, and the hardened container that runs on Cloud Run.

A tour of how astro-gcp-cloudrun-starter is put together — the Terraform layers and modules, the keyless CI/CD pipeline, and the hardened container that runs on Cloud Run.

While it’s quick to deploy, this template has a fair amount going on internally. This page documents the moving parts so you can adapt them with confidence.

Repository layout

src/                      # Astro v6 site (pages, components, content, config.yaml)
Dockerfile                # multi-stage: Node 22 build → nginx:8080 serve (~25MB, non-root)
.github/workflows/        # deploy-dev / staging / prod (keyless, app-deploy-only)
terraform/
  bootstrap/              # one-time seed: projects, state bucket, automation SA
  environments/           # shared, dev, staging, production layers
  modules/                # iam, cloud_run, artifact_registry, monitoring, logging,
                          # secret_manager, cost_governance, networking, ...
docs/                     # SETUP_PLAYBOOK, FRESH_USER_WALKTHROUGH, IAC_RULES, GAP_REGISTER

The Terraform layers

The infrastructure is deliberately split so that the dangerous, one-time setup is isolated from day-to-day changes:

  • bootstrap runs once with local state, creates the four projects + the GCS state bucket + the sa-terraform-admin automation account, then migrates its own state into GCS. It also enables the seed APIs that every later step depends on (including iamcredentials and cloudbilling).
  • shared holds the cross-cutting resources: the Workload Identity pool/provider (scoped to this repo), the GitHub deploy service account, and the Artifact Registry.
  • environments/* each adopt their bootstrap-created project (create_project = false) and compose the modules into a running environment.

Notable modules

  • iam — runtime and deploy service accounts, the WIF pool/provider, and the cross-project grants that let the deploy SA ship Cloud Run revisions and pull images from the shared registry.
  • cloud_run — the v2 service with enforced resource limits, scale-to-zero, CPU throttling, liveness/startup probes, and optional Secret-Manager-backed env vars.
  • cost_governance — a per-project billing budget with 50/80/100% alerts.
  • monitoring / logging — uptime checks, an availability SLO, alert policies, and audit log export.
  • Feature-gated modules (networking, cloud_sql, dns, load_balancer, cloud_armor, vertex_ai) are off by default for cost and are enabled per-environment via tfvars.

The CI/CD pipeline

The deploy workflows authenticate to GCP with Workload Identity Federation — no stored secrets. They build the container, push it to Artifact Registry tagged with the commit SHA, deploy a new Cloud Run revision, and smoke-test the public URL.

Crucially, the pipeline is app-deploy-only: it ships images but does not run terraform apply. Infrastructure is changed deliberately from a controlled environment, which keeps the CI identity least-privilege — it cannot alter or destroy your infrastructure.

The container

A multi-stage Dockerfile builds the static Astro site with Node 22, then serves dist/ with nginx on port 8080 as a non-root user. The result is a ~25MB image with a health check. To use a different framework, keep that contract — build your app and listen on 8080 — and the rest of the template carries over unchanged.

Where to go next

Back to Blog

Related Posts

View All Posts »