← All posts

Building this site: a serverless portfolio on GCP

2026-09-29 · Harshit Singh

This site is my Cloud Resume Challenge build, and it's also the demo. The visitor counter at the bottom of the page is a live transaction against a real backend — here is how the whole thing works, and the decisions that shaped it.

The request path

The static site is served by Firebase Hosting (Google's edge CDN with managed TLS). The counter is a Rust (axum) API in a 9 MB distroless container on Cloud Run, which runs a Datastore transaction — begin, read, commit with retry on conflict — against Firestore in Datastore mode. No lost increments, even under parallel load; there's a 50-way concurrency test in the repo proving it.

Why no load balancer

The classic challenge architecture puts a GCS bucket behind a global HTTPS load balancer with Cloud CDN. I built that first, then priced it: the forwarding rule alone is about $18/month. Firebase Hosting gives the same properties — global edge caching, managed certs, custom domain — at $0 within the free tier. Same story, better economics. Every significant choice like this is written down as an ADR in the repo's notes/decisions.md.

Keyless CI/CD

No service-account JSON keys exist anywhere. GitHub Actions mints a 5-minute OIDC token per run, Google's Secure Token Service exchanges it (repo-scoped trust condition), and the pipeline impersonates a least-privilege CI service account. The runtime service account is separate and holds exactly one role: datastore.user. Failing unit tests block deploys; images deploy by digest, never by mutable tag.

What I'd tell someone starting

Source: github.com/hxrsh159/cloud-resume-challenge. More posts as new projects land.