Ever wanted to deploy your app to the cloud but got lost in a maze of documentation? Let's fix that! Today, we're deploying a simple server to Google Cloud Run with GitHub Actions automation. By the end of this guide, every push to your main branch will automatically deploy your app. Magic? Nope, just good DevOps!
Psst! This guide was created for the Cloud Run Hackathon. If you're participating, this tutorial will help you get started with the basics. Cloud Run supports services, jobs, and worker pools - perfect for building AI-powered apps with Gemini, handling background tasks, or processing streams from Pub/Sub.
What We're Building
A simple Node.js Express server that says "Hello from Cloud Run!" - perfect for testing deployments. Once you've got this working, you can swap it out for your own app.
Step 1: Your Super Simple Server
Create a file called server.js:
const express = require('express');
const app = express();
const PORT = process.env.PORT || 8080;
app.get('/', (req, res) => {
res.json({
message: 'Hello from Cloud Run!',
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV || 'development'
});
});
app.get('/health', (req, res) => {
res.json({ status: 'healthy' });
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
And your package.json:
{
"name": "cloud-run-demo",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.18.2"
}
}
That's it! Just 20 lines of actual server code. Simple, clean, deployable.
Step 2: Create Your Google Cloud Project
Head over to the Google Cloud Console and let's get cooking:
-
Create a new project
- Click on the project dropdown at the top
- Click "New Project"
- Give it a memorable name (like
my-awesome-app) - Save the Project ID - you'll need this later!
Step 3: Set Up Cloud Run
- Search for "Cloud Run" in the search bar and select the product
- Click "Deploy Container"
- Fill in the basics:
-
Service name:
hello-server(or whatever makes you happy) -
Region:
europe-west1(or choose your nearest region) - Authentication: Allow public access (for our demo)
- Container image URL: Leave empty for now (we'll add it via GitHub Actions)
Don't worry if you see an error about the missing container - that's expected! We'll fix it when we deploy.
Save these values:
- Service name (e.g.,
hello-server) - Region (e.g.,
europe-west1)
Step 4: Set Up Artifact Registry
This is where Docker will store your container images.
- Search for "Artifact Registry" and select the product
- Click "Create Repository"
- Configure:
- Format: Docker
-
Name:
my-app-repo(keep it simple!) - Region: Same as your Cloud Run region
- Mode: Standard
- Click Create
Save the repository name for later!
Step 5: Create a Service Account
This gives GitHub permission to deploy on your behalf.
- Search for "Credentials" and go to the product page
- Click "Create Service Account"
Name it:
github-deployerAdd these permissions:
When creating your service account (github-deployer), you’ll be asked to assign roles.
These roles define what your GitHub Actions workflow is allowed to do inside your Google Cloud project.
| Role | What it Does | Why It’s Needed |
|---|---|---|
| Artifact Registry Reader | Reads Docker images from the registry | So Cloud Run can fetch the image |
| Artifact Registry Writer | Pushes new images to the registry | So GitHub Actions can upload builds |
| Cloud Run Admin | Creates and updates Cloud Run services | To deploy your app |
| Service Account User | Acts as the runtime service account | To let Cloud Run run your container |
- Click on your new service account → "Keys" tab
- "Add Key" → "Create new key" → JSON
- Download the JSON file (keep it safe!)
Step 6: Configure GitHub Secrets
In your GitHub repository:
- Go to Settings → Secrets and variables → Actions
- Click "New repository secret"
- Add these five secrets:
| Secret Name | Value |
|---|---|
GCP_SA_KEY |
Paste the entire JSON file content |
PROJECT_ID |
Your Google Cloud project ID |
PROJECT_REGION |
Your region (e.g., europe-west1) |
ARTIFACT_REGISTRY_REPO |
Your Artifact Registry name |
CLOUD_RUN_SERVICE |
Your Cloud Run service name |
Step 7: Add the Dockerfile
Create a Dockerfile in your project root:
FROM node:20-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy application code
COPY server.js ./
# Cloud Run expects port 8080
EXPOSE 8080
# Start the server
CMD ["npm", "start"]
Clean, simple, and production-ready!
Step 8: Create the GitHub Actions Workflow
Create .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)'
Step 9: Deploy!
Now for the moment of truth:
git add .
git commit -m "Initial Cloud Run deployment"
git push origin main
Watch the magic happen in your GitHub Actions tab! In a few minutes, you'll have a live URL.
Your Project Structure
Here's what your repository should look like:
my-cloud-run-app/
├── .github/
│ └── workflows/
│ └── deploy.yml
├── server.js
├── package.json
└── Dockerfile
Testing Your Deployment
Once deployed, visit your Cloud Run URL (find it in the Actions output or Google Cloud Console):
curl https://your-service-url.run.app
# Returns: {"message":"Hello from Cloud Run!","timestamp":"2025-11-06T..."}
curl https://your-service-url.run.app/health
# Returns: {"status":"healthy"}
Troubleshooting Tips
Build fails?
- Check your Dockerfile syntax
- Ensure
package.jsonhas all dependencies - Verify the Node version matches (we're using Node 20)
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
NODE_ENVis handled properly
Cost Awareness
Cloud Run has a generous free tier:
- 2 million requests per month
- 360,000 GB-seconds of memory
- 180,000 vCPU-seconds
With min-instances: 0, you only pay when your app receives traffic. Perfect for side projects!
What's Next?
Now that you have a working deployment pipeline:
- Add environment variables: Use Google Secret Manager for API keys and secrets
- Add a custom domain: Make it look professional
- Set up monitoring: Cloud Run has built-in metrics
- Scale settings: Adjust min/max instances based on your needs
- Replace the demo: Swap in your actual application!
Level Up: Beyond Basic Services
Cloud Run isn't just for web services! Explore these deployment types:
- Jobs: Perfect for batch processing, data pipelines, or scheduled tasks that run to completion
- Worker Pools: Ideal for pull-based workloads like Kafka or Pub/Sub consumers
Conclusion
You now have a fully automated deployment pipeline! Every push to main triggers a build, test, and deploy cycle. Your app scales automatically, costs nothing when idle, and can handle massive traffic spikes.
The best part? This setup works for any Node.js app. Just replace our simple server with your actual application, adjust the Dockerfile if needed, and you're good to go!
Happy deploying! 🎊


















Top comments (0)