The Track
Phase 1 of 4·0% complete

Ship It

A Portfolio Site, Live on Cloud Run

From empty repo to a public URL that redeploys when you merge a pull request.

You build

A live site, auto-deployed on PR merge.

Core concept

Containers, Cloud Run, Terraform, Git & PRs, CI/CD.

Now the fun part. You'll create the repository, build your portfolio site as a container, describe its infrastructure with Terraform kept right in the repo, and learn the Git pull-request flow that every real engineering team uses. When you merge a pull request into main, a pipeline automatically deploys your site to Cloud Run — using the keyless auth you set up in Phase 0.

What you'll have at the end: a public *.run.app URL showing your portfolio, a repo where your whole infrastructure is code, and a pull-request workflow where merging to main deploys automatically. A link you can put on your resume.

Why it matters in industry

Paying for idle servers is waste. Platforms that scale to zero and back up on demand let small teams run real products for almost nothing and handle traffic spikes without capacity planning.

Example — A side-project SaaS serves thousands of users on Cloud Run for a few dollars a month, because it only runs — and only bills — when requests come in. The same architecture would scale to millions without a rewrite.

A few terms, in plain English

Container
Your app plus everything it needs to run, packaged into one portable image. “Works on my machine” becomes “works everywhere.”
Artifact Registry
GCP's storage for those container images. Your pipeline pushes here; Cloud Run pulls from here.

Step 1 · Repo & Site

Create the repository and build the site

Create the repo with the exact name your Phase 0 WIF binding trusts — they must match, or deploys will be rejected.

Then build the site itself. Use whatever stack you like — the architecture doesn't care. Next.js is a good default (it's easy to add the AI features from Phase 2 onward). Have Claude scaffold it from your real material, and get it running locally before moving on.

▸ Ask Claude

Create a new public GitHub repo called your-repo, clone it locally, and cd into it.

Uses the GitHub CLI (gh repo create). Use the repo name from your Phase 0 WIF setup.

▸ Ask Claude

Scaffold a Next.js portfolio site in this repo based on my resume and interests (I'll paste them). Give it a distinctive look — an unusual color palette and a layout that isn't just stacked sections — not a generic template. Then run it locally so I can see it.

Push it to feel like YOU. Get it running locally (npm run dev → localhost:3000) before moving on.

Terminal — create the repo & run locally
gh repo create your-repo --public --clone   # use your WIF repo name
cd your-repo
# …AI scaffolds the Next.js site…
npm run dev                                 # open http://localhost:3000

Step 2 · Containerize

Add a Dockerfile

A container image is how Cloud Run runs your site. Rather than hand-write it, ask Claude — and have it explain the result so you understand what a container actually is.

The one Cloud Run rule: your app must listen on the PORT env var (default 8080) on 0.0.0.0. That's the #1 first-deploy gotcha. Test the container locally before going further.

▸ Ask Claude

Write a production Dockerfile for this Next.js app, optimized for Cloud Run (small final image, listens on the PORT env var). Then explain what each stage does and how a container works, like I've never used Docker.

▸ Ask Claude

Build and run my Dockerfile locally and give me the URL to check it in my browser.

Under the hood: docker build then docker run -p 8080:8080. Confirm the site loads at localhost:8080.

Terminal — build & run the container
docker build -t portfolio .
docker run -p 8080:8080 portfolio   # open http://localhost:8080

Step 3 · Infrastructure as Code

What Terraform is, and why it lives in your repo

Terraform lets you describe your cloud infrastructure in files instead of clicking around the console. You write what you want — “a Cloud Run service, an image registry” — and Terraform figures out the API calls to make it real. This is infrastructure as code, one of the most important ideas in modern engineering.

Why it's worth it: reproducible (anyone can recreate your setup from the files), reviewable (infra changes go through the same PR review as code), and version-controlled (every change is in Git history; you can roll back).

Terraform's core loop is write → plan → apply. You write a description of the desired state, run terraform plan to preview exactly what it will create/change/destroy, then terraform apply to make it real. You'll keep all of it in a /terraform subdirectory inside your repo.

Step 4 · Write the Terraform

Describe your infrastructure

Have Claude create the /terraform files. Ask it to explain each resource as it writes them — that's how you learn what the infrastructure actually is. Then run the preview once locally to see the loop in action.

▸ Ask Claude

In a /terraform subdirectory, write Terraform for: (1) the Google provider pointed at PROJECT_ID and YOUR_REGION, (2) an Artifact Registry Docker repo, and (3) a Cloud Run service that scales to zero and is publicly reachable, taking the container image as a variable. Explain each resource and how the pieces connect.

▸ Ask Claude

Run terraform init and terraform plan in /terraform and explain what the plan output is telling me before I apply anything.

'plan' shows what WILL change without changing it — read it every time. This is Terraform's safety net.

terraform/main.tf (excerpt)
resource "google_cloud_run_v2_service" "site" {
  name     = "portfolio"
  location = var.region
  template {
    containers {
      image = var.image          # set by the pipeline each deploy
      ports { container_port = 8080 }
    }
    scaling { min_instance_count = 0 }   # scale to zero
  }
}
Terminal — preview before applying
cd terraform
terraform init
terraform plan     # shows what WILL change — changes nothing

Step 5 · The Git Workflow

Branches, pull requests, and merging

Here's the single most important habit in team-based coding: nothing reaches main without going through a pull request. main is the official version of your project — the one that gets deployed — so it should always be trustworthy. You never edit it directly. You work on a branch, then propose merging it back via a pull request (PR).

The flow: branch off main, commit your work in small labeled snapshots, push and open a PR, review the diff (yourself, on a solo project — the discipline still matters), then merge. The merge is the moment your changes join main — and the moment deployment kicks off.

The pull-request loop

Branch

off main

Commit

small snapshots

Pull request

propose the merge

Review

read the diff

Merge → deploy

to Cloud Run

Nothing reaches main except through a reviewed pull request — and merging is the moment deployment kicks off.

▸ Ask Claude

Create a branch called feature/initial-site, commit all my work with clear messages, push it, and open a pull request into main with a description of what changed and how I tested it.

Review the diff it shows before you approve.

▸ Ask Claude

Review this pull request: summarize what it changes, and flag anything risky or unclear before I merge it.

A great habit even solo. On real teams, AI reviewers now comment on PRs automatically alongside humans.

Terminal — the branch → PR → merge loop
git checkout -b feature/initial-site
git add -A && git commit -m "Initial portfolio site"
git push -u origin feature/initial-site
gh pr create --fill --base main
# …review the diff…
gh pr merge --squash --delete-branch   # merge = deploy

Step 6 · CI/CD

Deploy automatically when a PR merges

The last piece: a GitHub Actions workflow that runs when a pull request merges into main. It authenticates to GCP with the keyless WIF from Phase 0, builds and pushes your container image, then runs terraform apply from your /terraform folder to roll out the new version. No manual deploys, no stored keys.

Add the two values you saved in Phase 0 (the WIF provider name and deploy service-account email) as GitHub repository secrets, then have Claude write the workflow.

▸ Ask Claude

Add my WIF_PROVIDER and DEPLOY_SA values as GitHub repo secrets, then write a GitHub Actions workflow at .github/workflows/deploy.yml that runs on push to main. It should authenticate to GCP with Workload Identity Federation (no keys), build and push my Docker image to Artifact Registry tagged with the commit SHA, and run terraform apply in /terraform with that image. Explain the workflow to me.

Merging a PR into main IS a push to main — so this fires exactly on merge.

Terminal — store the two Phase 0 values as secrets
gh secret set WIF_PROVIDER --body "projects/…/providers/github-provider"
gh secret set DEPLOY_SA   --body "deploy@PROJECT_ID.iam.gserviceaccount.com"
.github/workflows/deploy.yml (shape)
on: { push: { branches: [main] } }        # fires on PR merge
permissions: { contents: read, id-token: write }  # id-token = WIF
  # ...auth@v2 with workload_identity_provider + service_account
  # ...docker build & push  $IMAGE
  # ...terraform -chdir=terraform apply -auto-approve -var=image=$IMAGE

Step 7 · Go Live

Merge, and watch it deploy

Merge your Step 5 pull request into main. Open the Actions tab in GitHub and watch the job run: auth → build → push → terraform apply. When it's green, get your live URL.

▸ Ask Claude

Get the public URL of my deployed Cloud Run service and open it.

That's your site, live on the internet, deployed by merging a pull request.

Terminal — get your live URL
gcloud run services describe portfolio \
  --region YOUR_REGION --format 'value(status.url)'

Step 8 · Iterate

Make it genuinely yours (open-ended)

This is where the site stops looking like a template and starts looking like you — and it's the loop you'll repeat most in real work. Every change now flows through the same cycle: branch → commit → PR → review → merge → auto-deploy.

A few directions: visual polish (palette, typography, spacing, mobile responsiveness); content (a projects section, a photo, a resume-download button, GitHub/LinkedIn links); small interactions (a dark-mode toggle, subtle scroll animations, a contact section). Keep PRs small — easier to review, easier to undo.

▸ Ask Claude

I want to [change X] on my site. Make the change on a new branch, open a PR, and once I approve, merge it so it deploys.

The natural stopping point is when you'd be comfortable putting the URL on a real resume or LinkedIn.

Done when

  • Repo created with the name your WIF binding trusts.
  • Portfolio site built, running locally, and containerized.
  • Infrastructure described in a committed /terraform folder.
  • You understand the branch → commit → PR → review → merge flow, and can drive it with Claude.
  • GitHub Actions deploys automatically on merge to main, keylessly.
  • A live public URL you're happy to share.
Knowledge Check

1.What is the #1 first-deploy gotcha for a container on Cloud Run?

2.In the Git workflow, what triggers an automatic production deploy?

3.Why run `terraform plan` before `terraform apply`?

Answer every question correctly to complete this phase.