DEV Community

Rashika Karki
Rashika Karki

Posted on • Edited on

Deploy to Google Cloud Run in Minutes

How I Set Up Cloud Run CI/CD in Under 30 Minutes for the Cloud Run Hackathon

This post was created for the Cloud Run Hackathon to document my deployment setup experience.

When I decided to participate in the Cloud Run Hackathon, one of my biggest concerns was getting lost in deployment configuration. I needed a reliable, automated deployment pipeline that wouldn't eat into my development time. Following this structured approach helped me get everything running in under 30 minutes with my FastAPI application.

Why This Mattered for the Hackathon

The hackathon rewards bonus points for using Cloud Run effectively, especially when running multiple services (frontend and backend). Setting up a proper CI/CD pipeline early meant I could focus on building my actual application instead of wrestling with deployment issues every time I made a change.

What I Built On

I started with a basic FastAPI server as a foundation. FastAPI's speed and automatic API documentation made it perfect for rapid development during the hackathon. Here's the minimal setup that got me started:

main.py - A basic health check endpoint and main route:

from fastapi import FastAPI
from datetime import datetime
import os

app = FastAPI()

@app.get("/")
async def root():
    return {
        "message": "Hello from Cloud Run!",
        "timestamp": datetime.utcnow().isoformat(),
        "environment": os.getenv("ENV", "development")
    }

@app.get("/health")
async def health():
    return {"status": "healthy"}
Enter fullscreen mode Exit fullscreen mode

requirements.txt - Minimal dependencies:

fastapi==0.104.1
uvicorn[standard]==0.24.0
Enter fullscreen mode Exit fullscreen mode

The 30-Minute Setup Process

Phase 1: Google Cloud Project Setup (10 minutes)

I headed to the Google Cloud Console and created a new project. The key was keeping track of every identifier I created - Project ID, region selection, service names. I chose europe-west1 as my region and stuck with it consistently.

Creating a new project:

Project dropdown

New project button

I gave it a memorable name and saved the Project ID for later use.

Phase 2: Setting Up Cloud Run (5 minutes)

I searched for "Cloud Run" in the console and clicked "Deploy Container":

Cloud Run search

Deploy container button

Configuration was straightforward:

Cloud Run configuration

  • Service name: fastapi-server
  • Region: europe-west1
  • Authentication: Public access for testing
  • Container image URL: Left empty (GitHub Actions would handle this)

The missing container image error was expected at this stage.

Phase 3: Artifact Registry Configuration (5 minutes)

I created a Docker repository in Artifact Registry:

Artifact Registry search

Create repository button

Artifact Registry config

Configuration:

  • Format: Docker
  • Name: my-app-repo
  • Region: Same as Cloud Run (europe-west1)
  • Mode: Standard

Phase 4: Service Account and Permissions (8 minutes)

This was critical. I created a service account that would give GitHub permission to deploy on my behalf.

Credentials search

Create service account

I named it github-deployer and assigned four specific roles:

Role Purpose
Artifact Registry Reader Allows Cloud Run to fetch images
Artifact Registry Writer Allows GitHub Actions to push builds
Cloud Run Admin Enables deployment updates
Service Account User Lets Cloud Run execute containers

Service account permissions

Then I generated a JSON key:

Keys tab

Create JSON key

Download key

This JSON file is sensitive - it grants deployment permissions.

Phase 5: GitHub Secrets Configuration (5 minutes)

I configured GitHub repository secrets under Settings → Secrets and variables → Actions:

GitHub secrets navigation

New secret button

Secret form

I added five secrets:

Secret Name Value
GCP_SA_KEY Entire JSON key file content
PROJECT_ID My Google Cloud project ID
PROJECT_REGION europe-west1
ARTIFACT_REGISTRY_REPO my-app-repo
CLOUD_RUN_SERVICE fastapi-server

All secrets added

Phase 6: Dockerfile and Workflow (2 minutes)

The Dockerfile was optimized for FastAPI:

FROM python:3.11-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY main.py .

# Cloud Run expects port 8080
EXPOSE 8080

# Start uvicorn server
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]
Enter fullscreen mode Exit fullscreen mode

The GitHub Actions workflow in .github/workflows/deploy.yml:

name: Deploy to Cloud Run

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  deploy:
    name: Deploy to Google Cloud Run
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_SA_KEY }}

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2

      - name: Configure Docker for Artifact Registry
        run: |
          gcloud auth configure-docker ${{ secrets.PROJECT_REGION }}-docker.pkg.dev

      - name: Build Docker image
        run: |
          docker build -t ${{ secrets.PROJECT_REGION }}-docker.pkg.dev/${{ secrets.PROJECT_ID }}/${{ secrets.ARTIFACT_REGISTRY_REPO }}/${{ secrets.CLOUD_RUN_SERVICE }}:${{ github.sha }} .
          docker tag ${{ secrets.PROJECT_REGION }}-docker.pkg.dev/${{ secrets.PROJECT_ID }}/${{ secrets.ARTIFACT_REGISTRY_REPO }}/${{ secrets.CLOUD_RUN_SERVICE }}:${{ github.sha }} \
                     ${{ secrets.PROJECT_REGION }}-docker.pkg.dev/${{ secrets.PROJECT_ID }}/${{ secrets.ARTIFACT_REGISTRY_REPO }}/${{ secrets.CLOUD_RUN_SERVICE }}:latest

      - name: Push Docker image to Artifact Registry
        run: |
          docker push ${{ secrets.PROJECT_REGION }}-docker.pkg.dev/${{ secrets.PROJECT_ID }}/${{ secrets.ARTIFACT_REGISTRY_REPO }}/${{ secrets.CLOUD_RUN_SERVICE }}:${{ github.sha }}
          docker push ${{ secrets.PROJECT_REGION }}-docker.pkg.dev/${{ secrets.PROJECT_ID }}/${{ secrets.ARTIFACT_REGISTRY_REPO }}/${{ secrets.CLOUD_RUN_SERVICE }}:latest

      - name: Deploy to Cloud Run
        run: |
          gcloud run deploy ${{ secrets.CLOUD_RUN_SERVICE }} \
            --image ${{ secrets.PROJECT_REGION }}-docker.pkg.dev/${{ secrets.PROJECT_ID }}/${{ secrets.ARTIFACT_REGISTRY_REPO }}/${{ secrets.CLOUD_RUN_SERVICE }}:${{ github.sha }} \
            --platform managed \
            --region ${{ secrets.PROJECT_REGION }} \
            --project ${{ secrets.PROJECT_ID }} \
            --allow-unauthenticated \
            --min-instances 0 \
            --max-instances 10 \
            --memory 512Mi \
            --cpu 1 \
            --timeout 300 \
            --port 8080

      - name: Show deployment URL
        run: |
          echo "Deployment successful!"
          gcloud run services describe ${{ secrets.CLOUD_RUN_SERVICE }} \
            --platform managed \
            --region ${{ secrets.PROJECT_REGION }} \
            --project ${{ secrets.PROJECT_ID }} \
            --format 'value(status.url)'
Enter fullscreen mode Exit fullscreen mode

First Deployment

I committed everything and pushed to main:

git add .
git commit -m "Initial Cloud Run deployment"
git push origin main
Enter fullscreen mode Exit fullscreen mode

The GitHub Actions workflow executed automatically. Within 3-4 minutes, I had a live deployment URL with FastAPI's automatic interactive API documentation available at /docs.

Final Project Structure

my-cloud-run-app/
├── .github/
│   └── workflows/
│       └── deploy.yml
├── main.py
├── requirements.txt
└── Dockerfile
Enter fullscreen mode Exit fullscreen mode

Testing the Deployment

Once deployed, I could access:

# Main endpoint
curl https://your-service-url.run.app
# Returns: {"message":"Hello from Cloud Run!","timestamp":"2025-11-09T...","environment":"development"}

# Health check
curl https://your-service-url.run.app/health
# Returns: {"status":"healthy"}

# Interactive API docs (browser)
https://your-service-url.run.app/docs
Enter fullscreen mode Exit fullscreen mode

FastAPI's automatic OpenAPI documentation was a huge win during the hackathon - I could test endpoints directly from the browser.

How This Helped My Hackathon Project

With this foundation in place, I could:

  1. Iterate quickly: Every push to main automatically deployed
  2. Test in production: Real Cloud Run environment from day one
  3. Scale easily: Adding a second service (frontend) followed the same pattern
  4. Debug efficiently: FastAPI's detailed error messages and /docs endpoint
  5. Focus on features: No more deployment anxiety

Adapting for Multiple Services

For the hackathon's bonus points, I needed both frontend and backend services. The pipeline made this straightforward:

  1. Created a separate frontend repository
  2. Set up another Cloud Run service with a similar workflow
  3. Connected services using Cloud Run's internal networking
  4. Both services deployed automatically on their respective branches
  5. Used CORS middleware in FastAPI to handle cross-origin requests:
from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Updated with actual frontend URL in production
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
Enter fullscreen mode Exit fullscreen mode

Cost Considerations

With min-instances: 0, the services scaled to zero when not in use. Cloud Run's free tier covers:

  • 2 million requests per month
  • 360,000 GB-seconds of memory
  • 180,000 vCPU-seconds

Perfect for hackathon projects with minimal costs during development.

Key Takeaways

What worked well:

  • FastAPI's speed made iteration rapid
  • Having all configuration in one place (GitHub secrets)
  • Using standard Docker practices
  • Automated deployment removed manual errors
  • Service account permissions were clearly defined
  • Built-in API documentation at /docs for testing

Common pitfalls I avoided:

  • Mismatched regions across services
  • Forgetting to save the Project ID early
  • Not testing the health endpoint before deploying
  • Exposing the wrong port (always use 8080 for Cloud Run)
  • Not installing uvicorn with the [standard] extras for production

FastAPI-specific considerations:

  • Using uvicorn instead of development server
  • Setting --host 0.0.0.0 to accept external connections
  • Including CORS middleware for frontend integration

Beyond the Basic Setup

Once the pipeline was running, I expanded my hackathon project with:

  • Environment variables via Google Secret Manager for API keys
  • Cloud Run Jobs for background processing tasks
  • Pub/Sub integration for event-driven workflows
  • Gemini API integration for AI features (bonus points)
  • FastAPI's dependency injection for database connections
  • Pydantic models for request/response validation

Performance Notes

FastAPI on Cloud Run performed excellently:

  • Cold start times: ~2-3 seconds with the slim Python image
  • Request latency: Sub-100ms for most endpoints
  • The async/await support in FastAPI worked seamlessly
  • Auto-scaling handled traffic spikes without issues

Troubleshooting Tips

Build fails?

  • Check your Dockerfile syntax
  • Ensure requirements.txt has all dependencies
  • Verify the Python version matches (using Python 3.11)

Deployment fails?

  • Verify all GitHub secrets are correct
  • Check the service account has the right permissions
  • Ensure regions match across all services

Container crashes?

  • Check Cloud Run logs in Google Cloud Console
  • Verify your app listens on port 8080
  • Ensure environment variables are handled properly

Conclusion

This 30-minute setup gave me a production-ready deployment pipeline that handled the infrastructure concerns, letting me focus on building my hackathon submission. The combination of GitHub Actions and Cloud Run meant I could iterate rapidly without deployment friction.

FastAPI was the perfect choice for the backend - its speed, automatic documentation, and type safety accelerated development significantly. The deployment pipeline ensured that every feature I built was live within minutes.

For anyone participating in the Cloud Run Hackathon, I recommend setting up this pipeline first. Get your deployment working, then build your features. The automation pays for itself immediately.

The complete code structure and workflow are ready to adapt for any FastAPI application. Just replace the simple server with your actual application logic, add your models and endpoints, adjust resource limits if needed, and deploy.

This post was created to document my Cloud Run deployment experience for the Cloud Run Hackathon.

Top comments (0)