Skip to content

Deploy on Cloud Run (Sandbox)

This guide deploys a single-node Scion Hub on a Cloud Run Instance. Agents run as Cloud Run Sandboxes inside the same Instance — one container image, one deploy command, no external database or storage to provision.

What you will set up:

Component Provided by Purpose
Hub + Broker Cloud Run Instance Control plane, API, web UI
Agent runtime Cloud Run Sandboxes Isolated agent containers
Database Embedded SQLite State (ephemeral)
Auth perimeter Identity-Aware Proxy (IAP) Zero-trust access control

You need a GCP project with billing enabled and the following APIs:

Terminal window
export PROJECT_ID="your-project-id"
gcloud services enable \
run.googleapis.com \
iap.googleapis.com \
iam.googleapis.com \
--project=$PROJECT_ID
Tool Verify
gcloud 582.0.0 or later (verified; 575.0.0 does not have beta run instances) gcloud version and gcloud beta run instances deploy --help
git git --version

The deploy requires two credentials. gcloud auth and Application Default Credentials (ADC) are separate credential stores — both must be configured:

Terminal window
gcloud auth login
gcloud auth application-default login
Credential Verify Used by
gcloud auth login gcloud auth list gcloud commands (instance create, IAM bindings)
gcloud auth application-default login gcloud auth application-default print-access-token REST API call that enables IAP (step 3b)

The identity running the deploy needs these IAM roles on the target project:

Role Why
roles/run.admin Create and update Cloud Run Instances
roles/iam.serviceAccountUser Attach a service account to the Instance (if using --service-account)
roles/iap.admin Enable IAP and bind access policies

Create a dedicated service account for the Instance rather than using the default Compute Engine SA. The default SA has overly broad permissions and uses a @developer.gserviceaccount.com email format that, while supported, is not recommended for production use.

Terminal window
export SA_NAME="scion-instance"
gcloud iam service-accounts create $SA_NAME \
--display-name="Scion Hub Instance" \
--project=$PROJECT_ID
export SA_EMAIL="${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com"

Grant the minimum roles the Instance needs:

Role Why
roles/storage.admin Read/write workspace storage backends
roles/iam.serviceAccountAdmin Manage hub-minted service accounts for agents
roles/iam.serviceAccountTokenCreator Mint short-lived tokens for agent GCP identity (assign mode)
roles/aiplatform.user Vertex AI inference (if using Gemini-based agents)
Terminal window
for ROLE in roles/storage.admin roles/iam.serviceAccountAdmin \
roles/iam.serviceAccountTokenCreator roles/aiplatform.user; do
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${SA_EMAIL}" \
--role="${ROLE}"
done

Pass the service account to the deploy command with --service-account $SA_EMAIL (see Section 1).

The deploy requires a pre-built omni image — a single image containing the Hub and all supported harnesses. There is no public image; you must build your own.

Build your own image with Cloud Build:

The repository includes a Cloud Build config that builds the full omni image chain. Before building, create an Artifact Registry repository to push to:

Terminal window
gcloud artifacts repositories create scion \
--repository-format=docker \
--location=us-central1 \
--project=$PROJECT_ID

Then submit the build:

Terminal window
git clone https://github.com/GoogleCloudPlatform/scion.git
cd scion
gcloud builds submit \
--project=$PROJECT_ID \
--config=image-build/cloudbuild-omni.yaml \
--substitutions="_TAG=$(git rev-parse --short HEAD),_SHORT_SHA=$(git rev-parse --short HEAD),_COMMIT_SHA=$(git rev-parse HEAD),_REGISTRY=us-central1-docker.pkg.dev/$PROJECT_ID/scion" \
--ignore-file=image-build/gcloudignore-omni \
.

The build runs eight images in a chain (thick-prep → scion-base → claude → codex → opencode → antigravity → grok-build → omni). Each step builds in the Cloud Build worker and feeds the next. Only the final scion-omni image is pushed to your registry. Workers start fresh each run — there is no warm cache between builds.

Monitor progress in the Cloud Build console or with gcloud builds list --project=$PROJECT_ID.

When it completes, your image is:

us-central1-docker.pkg.dev/$PROJECT_ID/scion/scion-omni:$(git rev-parse --short HEAD)

The deploy derives the agent image registry from --image automatically; if derivation fails, the error names --image-registry as the explicit override. When this value is wrong, agent creation fails — not the deploy itself.

Stay in the clone directory for the rest of this guide — the deploy script and teardown script are repository-relative paths.


A single command creates the Instance, enables IAP, and verifies the perimeter:

Terminal window
./scripts/single-node/deploy.sh \
--name my-scion-hub \
--project $PROJECT_ID \
--region us-east4 \
--service-account $SA_EMAIL \
--image us-central1-docker.pkg.dev/YOUR_PROJECT/scion/scion-omni:YOUR_TAG

What the command does, step by step:

  1. Resolves your gcloud identity and the project number.
  2. Creates the Cloud Run Instance with --sandbox-launcher enabled (this is what allows agents to run as sandboxes inside the Instance).
  3. Enables IAP via a REST API PATCH (iapEnabled: true, invokerIamDisabled: true).
  4. Waits for IAP enforcement to activate (~30–75 seconds).
  5. Binds your identity as an IAP-authorized user.
  6. Asserts the perimeter — fetches the Instance URL with no credentials and fails the deploy if the app answers. This is the safety gate.
  7. Prints the Instance URL.
Flag Default Description
--region us-east4 GCP region
--cpu 4 CPU allocation
--memory 8Gi Memory allocation
--admin-email deployer’s gcloud account Override the Hub admin email
--service-account (default compute SA) GCP service account for the Instance. A custom SA is strongly recommended.
--image-registry derived from --image Override the image registry the broker uses to pull agent images

Open the Instance URL printed by the deploy command in your browser:

https://my-scion-hub-PROJECT_NUMBER.us-east4.run.app
  1. IAP challenge — Google sign-in. Use the email that was bound as the IAP user during deploy (your gcloud account, or the --admin-email value).
  2. Hub access — After sign-in you land directly in the Hub. There is no second login. The deployer is automatically seeded as the first admin and receives the super-admin role binding needed for full admin UI access.

From the Hub web UI, click New Project. Provide a name and a git remote URL (e.g. a GitHub repository the agent will work in).

Create an agent via the web UI or the API. The web UI is the simplest path — click a project, then New Agent, pick a harness (e.g. Claude), and start it.

Once the agent reaches a running state, click Attach in the web UI to open a live tmux session in your browser. You can watch the agent work, send it messages, and inspect its output in real time.


Instance size Idle agents Working agents
4 CPU / 8 GiB (default) 20 6
8 CPU / 32 GiB (maximum) 51 14

8 CPU / 32 GiB is the largest size Cloud Run allows. Larger deploys are refused.

Each number is a single observation — one stress-test run per size. Repeatability is unmeasured. These are the points past which the Instance was observed to fail, not thresholds to design against.

Do not extrapolate a per-CPU or per-GiB rule. Four times the memory and twice the CPU bought about three times the idle capacity and about twice the working capacity. Two points cannot establish a curve, and the relationship is not linear in either resource.

The numbers are context. The operating signal is create latency. Agent creates under two seconds mean headroom. Creates at ten seconds or more mean the Instance is near its ceiling — stop adding agents. That rule was measured at both sizes and, unlike a headcount, adapts to what the agents are actually doing. See the overload warning below for details.

Sizing to the measured ceiling is not the safe choice. Running fewer agents than you could costs only capacity. Running more destroys every workspace on the Instance with no warning and no recovery. The two errors are not the same size.

There are no per-agent resource limits. All agents share the Instance’s CPU and memory budget. A single compute-heavy agent can starve its neighbours.

To change the Instance size:

Terminal window
./scripts/single-node/deploy.sh \
--name my-scion-hub \
--project $PROJECT_ID \
--cpu 8 --memory 32Gi \
--image us-central1-docker.pkg.dev/YOUR_PROJECT/scion/scion-omni:YOUR_TAG

This tier is Tier 0: pure ephemeral.

  • Workspaces live on the Instance’s ephemeral filesystem.
  • The SQLite database (projects, agent metadata) lives on the same ephemeral filesystem.
  • The admin seed (your email) is set by an environment variable in the deploy command, so it is re-established automatically.

All state on the Instance is destroyed when the container restarts — whether by a planned redeploy, an overload that crashes the container, or any other restart. A redeploy you can plan for; a crash you cannot. The only durable copy of any agent’s work is what it has pushed to a git remote.

This is a deliberate design trade for fast, cheap, disposable deployments — not an oversight. Treat the Instance as a workspace, not as infrastructure. If you need durable workspaces, use the VM (GCE) path or the HA tier.


When running agents as Cloud Run Sandboxes, Scion enforces security hardening and standardized mounting conventions to guarantee compatibility with modern harnesses.

To align with secure-by-default execution practices, the Cloud Run Sandbox runtime executes agents as non-root users (UID 1000, matching the built-in scion user inside the base images), even though the host-side launcher process executes as root (UID 0).

  • Harness Compatibility: This hardening is critical for newer harnesses like Claude Code (≥ 2.1.246), which explicitly refuse to start with root or sudo privileges (even when configured with --dangerously-skip-permissions).
  • Environment Injection: The runtime explicitly maps and configures:
    • SCION_HOST_UID & SCION_HOST_GID to 1000 so that sciontool init drops privileges.
    • HOME directed to the agent-specific sandbox-mounted directory (e.g. ~/.gemini).
    • USER and LOGNAME environment variables forced to scion.
  • Writability: The broker ensures all agent homes, workspaces, and cached directories are pre-chowned to UID 1000 so the non-root runner can write and modify code without permission blocks.

Shared directories within the Cloud Run Sandbox are mounted at standard paths matching the Kubernetes runtime conventions, ensuring configuration portability across Single-Node and HA environments.

  • Standard Mount Path: All shared directories are mapped to /scion-volumes/<name> inside the sandbox container.
  • Workspace & Read-Only Support: The sandbox runtime fully supports mounting shared volumes within the workspace directory tree (using in_workspace: true to mount at /workspace/.scion-volumes/<name>) and enforces read-only locks when specified.

Delete the Instance:

Terminal window
./scripts/single-node/teardown.sh \
--name my-scion-hub \
--project $PROJECT_ID

Or directly:

Terminal window
gcloud beta run instances delete my-scion-hub \
--region=us-east4 \
--project=$PROJECT_ID \
--quiet

A Cloud Run Instance is billed for CPU and memory for the entire time it exists, regardless of whether it is handling requests. There is no scale-to-zero. Delete the Instance when you are not using it.

IAP access bindings are region-scoped, not per-instance. If this was the only instance in the region, review and clean up bindings:

Terminal window
gcloud iap web get-iam-policy \
--project=$PROJECT_ID \
--region=us-east4 \
--resource-type=cloud-run

If the Instance fails to start with a confusing image-pull error that names a cache mirror rather than your image, this is a known platform behavior (ptone/scion#1291). Verify:

  1. The image coordinate is correct (check for typos in the digest or tag).
  2. The image is accessible to the Instance’s service account.
  3. Re-run the deploy — transient pull failures sometimes resolve on retry.

The deploy command includes a perimeter assertion that fails the deploy if IAP is not enforcing. If you see the Instance URL responding without an IAP challenge:

Terminal window
# Check IAP status
curl -s -o /dev/null -w "%{http_code}" "https://INSTANCE_URL"
# Expected: 302 (redirect to Google sign-in)
# Bad: 200 (app is answering directly — IAP is not enforcing)

Re-run the deploy command — it is idempotent and will re-enable IAP.

Agent create returns 502: harness-config "antigravity" not found

Section titled “Agent create returns 502: harness-config "antigravity" not found”

If creating an agent without harnessConfig returns a 502 with failed to find harness-config "antigravity", the fix is to specify harnessConfig explicitly (e.g. "harnessConfig": "claude"). See the note in Section 3.