GCP

A starter recipe for self-hosted Flux on GCP — Cloud Run, Cloud SQL Postgres, Secret Manager.

A starter recipe for running Flux on Google Cloud. Cloud Run for compute, Cloud SQL for the database, Secret Manager for the bootstrap configuration. The same constraints from the Production checklist apply — most relevantly, PostgreSQL as the backend and session affinity if the server service scales past one instance.

Topology

Provisioning order

1. Networking and Cloud SQL

Create the Cloud SQL instance with private IP and connect it to a VPC. For the simplest setup, allow Cloud Run to reach it via the Cloud SQL connector (no VPC connector needed). For VPC-native traffic, add a Serverless VPC Access connector and configure both Cloud Run services to use it.

Create the flux database and the flux user with CREATE permission on the database.

2. Secret Manager

# Bootstrap token
gcloud secrets create flux-bootstrap-token --replication-policy=automatic
python -c 'import secrets; print(secrets.token_hex(32))' \
  | gcloud secrets versions add flux-bootstrap-token --data-file=-

# Encryption key
gcloud secrets create flux-encryption-key --replication-policy=automatic
python -c 'import secrets; print(secrets.token_hex(32))' \
  | gcloud secrets versions add flux-encryption-key --data-file=-

# Database URL (or build it from Cloud SQL Auth Proxy unix socket)
gcloud secrets create flux-database-url --replication-policy=automatic
echo -n "postgresql://flux:PASSWORD@/flux?host=/cloudsql/PROJECT:REGION:INSTANCE" \
  | gcloud secrets versions add flux-database-url --data-file=-

Grant the Cloud Run service account roles/secretmanager.secretAccessor on each secret.

3. Container image

Build your image (see Docker for the Dockerfile) and push to Artifact Registry:

gcloud builds submit --tag REGION-docker.pkg.dev/PROJECT/flux/flux:0.56.0 .

4. Cloud Run server service

gcloud run deploy flux-server \
  --image REGION-docker.pkg.dev/PROJECT/flux/flux:0.56.0 \
  --region REGION \
  --platform managed \
  --service-account flux-runtime@PROJECT.iam.gserviceaccount.com \
  --min-instances 1 \
  --max-instances 1 \
  --add-cloudsql-instances PROJECT:REGION:INSTANCE \
  --set-secrets FLUX_DATABASE_URL=flux-database-url:latest \
  --set-secrets FLUX_WORKERS__BOOTSTRAP_TOKEN=flux-bootstrap-token:latest \
  --set-secrets FLUX_SECURITY__ENCRYPTION__ENCRYPTION_KEY=flux-encryption-key:latest \
  --set-env-vars FLUX_SECURITY__AUTH__API_KEYS__ENABLED=true \
  --no-allow-unauthenticated   # or --allow-unauthenticated, depending on your auth model

--max-instances 1 keeps the starter simple, not safe — server instances coordinate through PostgreSQL (the scheduler is an advisory-lock singleton per cycle, so two instances never double-fire a cron). If you raise it, add --session-affinity so each worker’s SSE connection sticks to one instance, and account for instances × 40 connections of pool ceiling against Cloud SQL. The --no-allow-unauthenticated flag forces IAM-based access; if you’re using Flux’s own API-key auth as the only layer, set --allow-unauthenticated and rely on Flux to reject unauthenticated requests at the application layer.

5. Cloud Run worker service

gcloud run deploy flux-worker \
  --image REGION-docker.pkg.dev/PROJECT/flux/flux:0.56.0 \
  --region REGION \
  --command flux \
  --args start,worker,worker-1 \
  --service-account flux-runtime@PROJECT.iam.gserviceaccount.com \
  --min-instances 1 \
  --max-instances 10 \
  --set-secrets FLUX_WORKERS__BOOTSTRAP_TOKEN=flux-bootstrap-token:latest \
  --set-env-vars FLUX_WORKERS__SERVER_URL=https://flux-server-xxx.run.app

Workers scale freely on Cloud Run. The --args worker-1 is a starter — for multiple replicas you’ll want a unique name per instance, which Cloud Run doesn’t expose cleanly. In practice you may prefer GKE for worker fleets where naming and label-based routing matter.

Cloud Run caveats

Beyond scale-to-zero:

The honest summary: Cloud Run works for low-volume Flux deployments where you want serverless billing. For anything load-bearing, GKE is the better answer.

Secrets

Same separation as on AWS:

What can go wrong

Cold-start drops scheduled fires

Symptom. Cron schedules miss fires; Cloud Run logs show the instance count dropped to zero between cron times.

Cause. min-instances: 0 (default). The scheduler is a background task in the server process; if the instance is scaled to zero, nothing polls the schedule table.

Fix. gcloud run services update flux-server --min-instances 1.

Server unreachable from workers

Symptom. Worker logs show ConnectError to the server’s .run.app URL.

Cause. Worker service account lacks roles/run.invoker on the server service (when the server is --no-allow-unauthenticated), or the URL is wrong.

Fix. Grant roles/run.invoker to the worker service account on the server service, and verify FLUX_WORKERS__SERVER_URL matches the server’s current URL (it changes on first deploy).

Cloud SQL connections exhausted

Symptom. Server logs OperationalError: connection ... aborted under load.

Cause. Cloud Run instances can spin up many concurrent connections; the default Cloud SQL connection limit on small instance types is low.

Fix. Either size up the Cloud SQL instance, or front Cloud SQL with PgBouncer running on GKE. Tune FLUX_DATABASE_POOL_SIZE / FLUX_DATABASE_MAX_OVERFLOW (defaults 20/20 per instance) to fit within the per-instance budget.


This recipe is derived from GCP service documentation and Flux’s deployment requirements; the author hasn’t personally validated it end-to-end on GCP as of 2026-07. Treat IAM bindings, VPC connectors, and Cloud Run flag combinations as starting points to validate in a sandbox project before promoting.