Deployment Workflow
The ci-deploy.yml is a reusable workflow that handles Docker build, push, and Cloud Run deployment.
Inputs
on:
workflow_call:
inputs:
environment:
required: true
type: string
version:
required: true
type: string
terraform-outputs-artifact-id:
required: true
type: string
terraform-outputs-file-name:
required: true
type: string
build-artifact-name:
required: true
type: string
Deployment Steps
1. Download Artifacts
- uses: actions/download-artifact@v4
with:
run-id: ${{ inputs.terraform-outputs-artifact-id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: ${{ inputs.build-artifact-name }}
2. Parse Terraform Outputs
Extracts deployment configuration from Terraform:
- name: Setup Terraform Outputs Variables
run: |
OUTPUTS=$(cat ${{ inputs.terraform-outputs-file-name }})
echo "BACKSTAGE_CLOUD_SQL_CONNECTION_NAME=$(echo $OUTPUTS | jq -r '.backstage_cloud_sql_instance_connection_name.value')" >> $GITHUB_ENV
echo "BACKSTAGE_DB_NAME=$(echo $OUTPUTS | jq -r '.backstage_db_name.value')" >> $GITHUB_ENV
# ... additional outputs
Extracted Values:
- Cloud SQL connection name
- Database name and user
- DB password secret ID
- IAP audience
- Service account email
- VPC connector ID
- Vault CA cert secret ID
- Vault internal URL
- TechDocs bucket
3. Authenticate to GCP
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
service_account: ${{ secrets.WIF_SERVICE_ACCOUNT }}
Uses Workload Identity Federation for secure, keyless authentication.
4. Retrieve Secrets
- name: Get Backstage DB Password
run: |
gcloud secrets versions access latest \
--secret="${{ env.BACKSTAGE_DB_PASSWORD }}" | tr -d '\n' | \
{ read -r pw; echo "::add-mask::$pw"; echo "BACKSTAGE_DB_PASSWORD_VALUE=$pw" >> $GITHUB_ENV; }
- name: Get Vault CA Certificate
run: |
gcloud secrets versions access latest \
--secret="${{ env.VAULT_CA_CERT_SECRET_ID }}" > /tmp/vault-ca.crt
5. Generate Configuration
Templates the production configuration file:
- name: Generate Backstage Config
working-directory: backstage
env:
# All environment variables from Terraform outputs and secrets
run: |
envsubst < app-config.production.yaml.tpl > app-config.production.yaml
Environment Variables Substituted:
- Database connection details
- OAuth credentials (GitHub, Google)
- Vault configuration
- JWT key paths
- Base URLs
6. Build and Push Docker Image
- uses: docker/build-push-action@v6
with:
context: backstage
file: backstage/packages/backend/Dockerfile
push: true
tags: ${{ env.GAR_URL }}
cache-from: type=gha
cache-to: type=gha,mode=max
Image is pushed to Google Artifact Registry.
7. Deploy to Cloud Run
- name: Deploy to Cloud Run
run: |
gcloud run deploy backstage \
--image="${{ env.GAR_URL }}" \
--project="${{ secrets.GCP_PROJECT_ID }}" \
--region="${{ env.REGION }}" \
--port=7007 \
--service-account="${{ env.BACKSTAGE_SERVICE_ACCOUNT_EMAIL }}" \
--add-cloudsql-instances="${{ env.BACKSTAGE_CLOUD_SQL_CONNECTION_NAME }}" \
--vpc-connector="${{ env.BACKSTAGE_VPC_CONNECTOR_ID }}" \
--set-secrets="/etc/ssl/certs/vault-ca.crt=${{ env.VAULT_CA_CERT_SECRET_ID }}:latest" \
--set-secrets="/etc/backstage/jwt-private/key=backstage-jwt-private-key:latest" \
--set-secrets="/etc/backstage/jwt-public/key=backstage-jwt-public-key:latest" \
--memory=4Gi \
--cpu=4000m \
--timeout=300 \
--concurrency=80 \
--min-instances=1 \
--max-instances=1 \
--allow-unauthenticated
Cloud Run Configuration
| Setting | Value | Purpose |
|---|---|---|
| Memory | 4Gi | Node.js + TypeScript compilation |
| CPU | 4000m | 4 cores for parallel processing |
| Timeout | 300s | Long-running API calls |
| Concurrency | 80 | Requests per instance |
| Min Instances | 1 | Avoid cold starts |
| Max Instances | 1 | Cost control |
| Auth | Unauthenticated | Uses Backstage's own auth |
Mounted Secrets
| Path | Secret | Purpose |
|---|---|---|
/etc/ssl/certs/vault-ca.crt | vault-ca-cert | Vault TLS verification |
/etc/backstage/jwt-private/key | backstage-jwt-private-key | Token signing |
/etc/backstage/jwt-public/key | backstage-jwt-public-key | Token verification |
Permissions
permissions:
contents: read
id-token: write
Minimal permissions - only needs to read repo and use OIDC.