Skip to content

Cloud Run cost tier (cloud-cheap)

The repository’s cloud-cheap tier runs DeepIntShield as one multi-container Google Cloud Run service backed by Cloud SQL. It is intended for development, demos, and low-traffic environments that prioritize a low idle cost and can tolerate cold starts.

One Cloud Run revision contains four containers that share a network namespace:

ComponentRolePersistence
serverHTTP ingress on port 8080; gateway and embedded regex/Rego guard runtimeApplication state in Cloud SQL
modelsML detectors on localhost; Hugging Face cache mounted from GCS with gcsfuseModel weights in the GCS bucket
redisResponse/semantic cache sidecarEphemeral; not a system of record
openfgaRequired relationship-authorization service on localhost port 8082Separate OpenFGA database/user in Cloud SQL

The app database and OpenFGA database share one managed PostgreSQL instance but use separate databases and users. Authorization stores, authorization models, and tuples therefore survive scale-to-zero, revisions, and additional Cloud Run instances.

  • A Google Cloud project with billing enabled and gcloud authenticated.
  • Permission to create Cloud Run services/jobs, Cloud SQL, Artifact Registry, Secret Manager secrets, a GCS bucket, service accounts, and IAM bindings.
  • A globally unique model-cache bucket name.
  • A public DNS hostname that can be mapped to Cloud Run.
  • Docker for local native-amd64 builds, or Cloud Build access.

Run these commands from the repository root:

  1. Create the deployment environment file.

    Terminal window
    cp deployment/tiers/cloud-cheap/env.example \
    deployment/tiers/cloud-cheap/.env

    Set at least PROJECT_ID, REGION, globally unique MODELS_BUCKET, CLOUDSQL_PASSWORD, PUBLIC_HOSTNAME, SMTP values, and DEPLOYER_MEMBER. PUBLIC_HOSTNAME is a hostname only—no scheme, port, wildcard, or path.

  2. Bootstrap shared resources once.

    Terminal window
    ./deployment/tiers/cloud-cheap/scripts/bootstrap_cloudrun.sh

    The script creates the app and OpenFGA Cloud SQL databases/users, model bucket, runtime service account, and required IAM bindings. It also creates independent stable encryption and OpenFGA database-password secrets when they do not already exist.

  3. Build and push the images.

    Terminal window
    ./deployment/tiers/cloud-cheap/scripts/build_and_push.sh

    The target is linux/amd64. On Apple silicon or another arm64 host, leave BUILD_BACKEND=auto so the script selects native-amd64 Cloud Build. Ordinary QEMU emulation can crash the Go runtime during go mod download; that crash is not evidence that go.mod or go.sum should change.

  4. Provision OpenFGA, migrate, and deploy.

    Terminal window
    ./deployment/tiers/cloud-cheap/scripts/deploy_cloudrun.sh

    Deployment runs idempotent OpenFGA database provisioning and a version-matched schema migration job before replacing the service. The OpenFGA image is pinned by immutable digest.

  5. Verify the deployed revision.

    Terminal window
    ./deployment/tiers/cloud-cheap/scripts/smoke_test.sh

    The smoke test validates the deployed manifest, required OpenFGA startup dependency, persistent datastore wiring, cache policy, scaling values, health endpoint, and the GAF-enabled startup record. It allows several minutes for a cold start.

Map PUBLIC_HOSTNAME to the Cloud Run service and publish the DNS records that Google provides. If an organization policy blocks an anonymous run.invoker binding, the deployment can still be tested through an authenticated Cloud Run proxy, but public gateway clients will not reach it until that policy is resolved.

OpenFGA is not optional in this tier:

  • AGENTICNEW_GAF_REQUIRED=true makes the server fail startup instead of serving protected routes without a database-ready authorizer.
  • Cloud Run starts the server only after the OpenFGA /healthz startup probe succeeds.
  • Legacy ReBAC and Agentic-New GAF use separate stores on the same durable OpenFGA database.
  • OpenFGA query/iterator caches and the process-local GAF Allow cache are disabled, so a stale revision-local Allow cache cannot hide a revocation from the next check.

JSON-RPC /mcp, POST /v1/mcp/tool/execute, Agent Mode, and nested Code Mode calls use the same canonical GAF enforcement point. Every actual invocation requires an exact Allow. A dependency or durable-audit failure blocks execution instead of falling back to an ungoverned path.

Disabling caches does not synchronously terminate every in-flight operation on every replica. An OAuth status/generation or relationship change is enforced on the next acquisition, decision, or reconciliation; an already leased call may drain. See Relationship Authorization and MCP tool execution.

The manifest derives APP_BASE_URL=https://<PUBLIC_HOSTNAME> from deployment configuration. That stable public origin owns both:

  • /api/oauth/callback; and
  • the exact public GET /api/oauth/client-metadata.json used for deployment- owned Client ID Metadata Document registration.

The request Host and forwarded headers cannot choose these identifiers. Keep the generated DEEPINTSHIELD_ENCRYPTION_KEY stable on every revision: strict OAuth 2.1 refuses initiation without it, and uncoordinated rotation makes persisted client secrets, PKCE verifiers, tokens, and pending configurations unreadable. The deployment scripts reuse the enabled Secret Manager version and do not rotate it automatically.

For registration priority, issuer/resource binding, reauthorization, and revocation behavior, see MCP OAuth 2.1 and OAuth 2.0.

The defaults are deliberately cost-first:

CLOUD_RUN_MIN_INSTANCES=0
CLOUD_RUN_MAX_INSTANCES=1
CLOUD_RUN_CONCURRENCY=40
CLOUDSQL_TIER=db-f1-micro
  • MIN_INSTANCES=0 removes idle Cloud Run compute but retains the standing Cloud SQL, GCS, Artifact Registry, and networking costs. Verify current regional prices with the Google Cloud calculator; a fixed monthly total is not guaranteed.
  • Set MIN_INSTANCES to at least 1 to reduce idle cold starts, accepting the additional compute cost. New revisions and scale-out can still cold-start.
  • Raise MAX_INSTANCES and tune concurrency for horizontal scaling only after upgrading Cloud SQL. The shared db-f1-micro app/OpenFGA instance is the primary bottleneck.
  • Redis caches and delegated Streamable HTTP session pools are replica-local. Scale-out can cause a cache miss or a fresh token exchange/initialization; use load-balancer stickiness when a workflow depends on one long-lived MCP session. Durable binding/status checks still run before a warm session is acquired.
  • Each streaming request is bounded by Cloud Run’s configured 3,600-second ceiling; provider, gateway, and client timeouts may end it sooner.
  • OpenFGA checks, policy work, instrumentation, cache misses, and upstream tools retain measurable workload-dependent latency. Benchmark with expected concurrency, policies, failures, and MCP session behavior.

The models container synchronously preloads configured ML detectors before it opens port 8093, while the ingress server starts in parallel. Until the models port is ready, this tier’s ML guardrail calls are configured fail-open; embedded regex and Rego checks continue to run. The GCS mount avoids repeatedly downloading warmed weights, but it does not eliminate process/model startup.

If fail-open ML warmup is unacceptable, keep instances warm and validate model readiness before traffic, or use an always-on deployment designed for that availability requirement.

  • Cloud SQL is the system of record. Export both the app and OpenFGA databases before a migration or teardown.
  • The OpenFGA database password is another stable Secret Manager value. The scripts reuse it and do not rotate it automatically; coordinate password rotation with the database and every revision.
  • The local filesystem and Redis sidecar are ephemeral. Durable webhook payloads require an external S3-compatible object store.
  • Langfuse export is optional; OpenFGA/GAF is required.
  • The production-cheap CI environment follows the same build, provision, migration, deploy, and smoke sequence as the manual scripts.