15 — First Deploy
This chapter walks through deploying to Cloud Run manually for the first time. After this, GitHub Actions will handle it automatically.
Prerequisites
Before deploying, make sure you have:
- All Terraform resources created (chapters 05-13)
- Secrets set in Secret Manager (chapter 09)
- Docker image built and pushed to Artifact Registry
- PlanetScale database created (chapter 04)
- Domain pointed to Cloud Run (or skip this for now — use the
.run.appURL)
Step 1: Authenticate Docker to Artifact Registry
Step 2: Build and push the Docker image
# Set the image URL
IMAGE="southamerica-east1-docker.pkg.dev/mycoolproject-prod/app-repo/app"
# Build with two tags
docker build -t $IMAGE:latest -t $IMAGE:$(git rev-parse --short HEAD) .
# Push both tags
docker push --all-tags $IMAGE
Step 3: Set secret values (if not done yet)
# Database URL (from PlanetScale dashboard)
echo -n "postgres://user:password@aws.connect.psdb.cloud/mycoolproject?sslmode=require" \
| gcloud secrets versions add DATABASE_URL --data-file=-
# Django secret key
python -c "import secrets; print(secrets.token_urlsafe(50))"
echo -n "<generated-key>" | gcloud secrets versions add DJANGO_SECRET_KEY --data-file=-
# ALLOWED_HOSTS
echo -n "mycoolproject.com,www.mycoolproject.com" | gcloud secrets versions add ALLOWED_HOSTS --data-file=-
Step 4: Verify Cloud Run is deployed
# Get the service URL
gcloud run services describe mycoolproject --region=southamerica-east1 --format="value(status.url)"
# Test the health endpoint
curl https://<url>/health/
Should return {"status": "ok"}.
Step 5: Run migrations on PlanetScale
Since we can't use gcloud sql (no Cloud SQL), we use a Cloud Run Job to run migrations:
# Create a temporary job to run migrations
gcloud run jobs create migrate \
--image=$IMAGE:latest \
--region=southamerica-east1 \
--service-account=mycoolproject-worker@mycoolproject-prod.iam.gserviceaccount.com \
--set-env-vars=DATABASE_URL=DATABASE_URL:latest,SECRET_KEY=DJANGO_SECRET_KEY:latest \
--command="uv,run,manage.py,migrate"
# Run it
gcloud run jobs execute migrate --region=southamerica-east1 --wait
Step 6: Collectstatic
Upload static files to Cloud Storage:
docker run --rm \
-e DATABASE_URL=DATABASE_URL:latest \
-e SECRET_KEY=DJANGO_SECRET_KEY:latest \
-e DJANGO_SETTINGS_MODULE=core.settings.prod \
-e GS_PROJECT_ID=mycoolproject-prod \
$IMAGE:latest \
uv run manage.py collectstatic --noinput
Wait, this won't work — it needs access to Secret Manager. Instead, use a Cloud Run Job:
# Create job for collectstatic
gcloud run jobs create collectstatic \
--image=$IMAGE:latest \
--region=southamerica-east1 \
--service-account=mycoolproject-worker@mycoolproject-prod.iam.gserviceaccount.com \
--set-secrets=DATABASE_URL=DATABASE_URL:latest,SECRET_KEY=DJANGO_SECRET_KEY:latest,GS_PROJECT_ID=mycoolproject-prod:latest \
--command="uv,run,manage.py,collectstatic,--noinput"
# Run it
gcloud run jobs execute collectstatic --region=southamerica-east1 --wait
Verify everything works
# Test the site
curl https://mycoolproject-<hash>-uc.a.run.app/
# Check logs
gcloud run services logs tail mycoolproject --region=southamerica-east1
If something goes wrong
Check the logs
Check Cloud Run revisions
Roll back to a previous image
# Get the previous image tag (from git history or Artifact Registry)
docker pull southamerica-east1-docker.pkg.dev/mycoolproject-prod/app-repo/app:<previous-sha>
# Update Cloud Run
gcloud run services update mycoolproject \
--image=southamerica-east1-docker.pkg.dev/mycoolproject-prod/app-repo/app:<previous-sha> \
--region=southamerica-east1
What's next
Now that the app is deployed, we need: - Custom domain + SSL (chapter 16) - Workload Identity Federation for GitHub Actions (chapter 17) - GitHub Actions CI/CD pipeline (chapter 18)
Navigation
- 01 — Introduction: What We're Building
- 02 — Terraform Overview
- 03 — Cloud Services Explained
- 04 — PlanetScale Database Explained
- 05 — Project Setup & Terraform State
- 06 — GCP Project & APIs
- 07 — Artifact Registry
- 08 — Secrets Management
- 09 — Cloud Storage
- 10 — Service Accounts & IAM
- 11 — Cloud Run
- 12 — Cloud Tasks & Scheduler
- 13 — Dockerfile
- 14 — First Deploy (Current chapter)
- 15 — Custom Domain & SSL
- 16 — Workload Identity Federation
- 17 — GitHub Actions CI/CD
- 18 — Quick Reference