KeyEnvKeyEnv

Google Cloud Run

Sync KeyEnv secrets to Google Cloud Run services.

Google Cloud Run Integration

Sync secrets from KeyEnv to your Cloud Run services.

Prerequisites

  • Google Cloud project with Cloud Run enabled
  • gcloud CLI installed and authenticated
  • KeyEnv service token with read access

Quick Sync

Sync secrets directly as environment variables:

export KEYENV_TOKEN=your-service-token

SERVICE_NAME="my-service"
REGION="us-central1"

# Export and set env vars
secrets=$(keyenv export -p YOUR_PROJECT_ID -e production --format shell)

gcloud run services update "$SERVICE_NAME" \
  --region="$REGION" \
  --update-env-vars="$secrets"

Sync Script

Create a reusable sync script:

#!/bin/bash
# sync-to-cloud-run.sh

set -euo pipefail

: "${KEYENV_TOKEN:?KEYENV_TOKEN is required}"

PROJECT_ID="${KEYENV_PROJECT:-YOUR_PROJECT_ID}"
ENVIRONMENT="${KEYENV_ENV:-production}"
SERVICE_NAME="${CLOUD_RUN_SERVICE:-my-service}"
REGION="${CLOUD_RUN_REGION:-us-central1}"

echo "Syncing KeyEnv ($ENVIRONMENT) to Cloud Run ($SERVICE_NAME)..."

# Export secrets
secrets=$(keyenv export -p "$PROJECT_ID" -e "$ENVIRONMENT" --format shell)

# Update Cloud Run service
gcloud run services update "$SERVICE_NAME" \
  --region="$REGION" \
  --update-env-vars="$secrets"

echo "Done! Cloud Run is redeploying..."

For production, store secrets in Google Secret Manager and reference them in Cloud Run:

1. Sync to Secret Manager

#!/bin/bash
# sync-to-secret-manager.sh

set -euo pipefail

GCP_PROJECT="your-gcp-project"

# Export secrets from KeyEnv
secrets=$(keyenv export -p YOUR_PROJECT_ID -e production --format json)

# Create/update each secret in Secret Manager
echo "$secrets" | jq -r '.secrets[] | "\(.key)=\(.value)"' | while IFS='=' read -r key value; do
  # Try to create, or add new version if exists
  if gcloud secrets describe "$key" --project="$GCP_PROJECT" &>/dev/null; then
    echo -n "$value" | gcloud secrets versions add "$key" --data-file=- --project="$GCP_PROJECT"
    echo "Updated: $key"
  else
    echo -n "$value" | gcloud secrets create "$key" --data-file=- --project="$GCP_PROJECT"
    echo "Created: $key"
  fi
done

2. Reference in Cloud Run

gcloud run services update my-service \
  --region=us-central1 \
  --update-secrets="DATABASE_URL=DATABASE_URL:latest,API_KEY=API_KEY:latest"

Or in your service.yaml:

apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: my-service
spec:
  template:
    spec:
      containers:
        - image: gcr.io/my-project/my-service
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: DATABASE_URL
                  key: latest
            - name: API_KEY
              valueFrom:
                secretKeyRef:
                  name: API_KEY
                  key: latest

CI/CD Integration

GitHub Actions

name: Deploy to Cloud Run

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write

    steps:
      - uses: actions/checkout@v4

      - name: Install KeyEnv CLI
        run: curl -fsSL https://keyenv.dev/install.sh | bash

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          workload_identity_provider: ${{ vars.WIF_PROVIDER }}
          service_account: ${{ vars.WIF_SERVICE_ACCOUNT }}

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

      - name: Sync secrets to Secret Manager
        run: |
          export PATH="$HOME/.keyenv/bin:$PATH"

          keyenv export -p ${{ vars.KEYENV_PROJECT }} -e production --format json | \
            jq -r '.secrets[] | "\(.key)=\(.value)"' | while IFS='=' read -r key value; do
              if gcloud secrets describe "$key" &>/dev/null; then
                echo -n "$value" | gcloud secrets versions add "$key" --data-file=-
              else
                echo -n "$value" | gcloud secrets create "$key" --data-file=-
              fi
            done
        env:
          KEYENV_TOKEN: ${{ secrets.KEYENV_TOKEN }}

      - name: Deploy to Cloud Run
        uses: google-github-actions/deploy-cloudrun@v2
        with:
          service: my-service
          region: us-central1
          image: gcr.io/${{ vars.GCP_PROJECT }}/my-service

Cloud Build

# cloudbuild.yaml
steps:
  - name: 'gcr.io/cloud-builders/curl'
    args: ['-fsSL', 'https://keyenv.dev/install.sh', '-o', 'install.sh']

  - name: 'ubuntu'
    entrypoint: 'bash'
    args: ['install.sh']

  - name: 'ubuntu'
    entrypoint: 'bash'
    args:
      - '-c'
      - |
        export PATH="$$HOME/.keyenv/bin:$$PATH"
        keyenv export -p $$KEYENV_PROJECT -e production --format json | \
          jq -r '.secrets[] | "\(.key)=\(.value)"' | while IFS='=' read -r key value; do
            gcloud secrets versions add "$$key" --data-file=- <<< "$$value" || \
            gcloud secrets create "$$key" --data-file=- <<< "$$value"
          done
    secretEnv: ['KEYENV_TOKEN']
    env:
      - 'KEYENV_PROJECT=your-project-id'

  - name: 'gcr.io/cloud-builders/gcloud'
    args: ['run', 'deploy', 'my-service', '--image', 'gcr.io/$PROJECT_ID/my-service', '--region', 'us-central1']

availableSecrets:
  secretManager:
    - versionName: projects/$PROJECT_ID/secrets/KEYENV_TOKEN/versions/latest
      env: 'KEYENV_TOKEN'

Multiple Services

Sync to multiple Cloud Run services:

#!/bin/bash

SERVICES=("api" "web" "worker")
REGION="us-central1"

secrets=$(keyenv export -p YOUR_PROJECT_ID -e production --format shell)

for service in "${SERVICES[@]}"; do
  echo "Syncing to $service..."
  gcloud run services update "$service" \
    --region="$REGION" \
    --update-env-vars="$secrets"
done

Terraform Integration

# main.tf
resource "google_secret_manager_secret" "secrets" {
  for_each  = toset(var.secret_keys)
  secret_id = each.key

  replication {
    auto {}
  }
}

resource "google_cloud_run_v2_service" "app" {
  name     = "my-service"
  location = "us-central1"

  template {
    containers {
      image = "gcr.io/my-project/my-service"

      dynamic "env" {
        for_each = var.secret_keys
        content {
          name = env.value
          value_source {
            secret_key_ref {
              secret  = google_secret_manager_secret.secrets[env.value].secret_id
              version = "latest"
            }
          }
        }
      }
    }

    service_account = google_service_account.app.email
  }
}

Best Practices

  1. Use Secret Manager - Better security and automatic rotation support
  2. Grant minimal permissions - Service accounts should only access needed secrets
  3. Use IAM conditions - Restrict secret access by service identity
  4. Enable audit logging - Track secret access in Cloud Audit Logs

Troubleshooting

Permission Denied

Grant the Cloud Run service account access to secrets:

gcloud secrets add-iam-policy-binding SECRET_NAME \
  --member="serviceAccount:[email protected]" \
  --role="roles/secretmanager.secretAccessor"

Service Not Restarting

Force a new revision:

gcloud run services update my-service --region=us-central1 --no-traffic
gcloud run services update-traffic my-service --region=us-central1 --to-latest

Secrets Not Mounting

Check the service configuration:

gcloud run services describe my-service --region=us-central1 --format=yaml

On this page