Skip to main content

Data Analyst Agent on Google Cloud Run

Data Analyst Agent on Google Cloud Run

The Data Analyst Agent lets a user upload a small data file (CSV, TSV, JSON, plain text, or Excel) and ask questions about it in plain language. Unlike a typical chat assistant, it never guesses: it writes real Python (pandas/numpy/matplotlib) and runs it — isolated inside Cloud Run's sandbox launcher (gVisor) — to compute the actual answer, and can return a genuine chart alongside its explanation. This module deploys the agent on Cloud Run v2 on top of the App_CloudRun foundation, which provisions and manages the shared Google Cloud infrastructure.

This guide focuses on the cloud services the agent uses and how to explore and operate them from the Google Cloud Console and the command line. For the mechanics common to every Cloud Run application — service identity, ingress and load balancing, scaling and concurrency, CI/CD, Cloud Armor, IAP, Binary Authorization, VPC Service Controls, backups, and the deployment lifecycle — refer to the App_CloudRun foundation guide rather than repeating them here.


What DataAnalyst costs on RAD, and how that compares​

DataAnalyst on RAD's Cloud Run module costs about US$17.58 a month in a Google Cloud project you own, plus a one-time 75-credit module fee (US$7.50). DataAnalyst is an ADK-based agent that writes and runs real Python against an uploaded data file, sandboxed inside Cloud Run.

If DataAnalyst is only needed occasionally — a demo, a trial, a seasonal or study workload — a RAD-managed project can be deleted and restored within 30 days for a handful of credits, so that US$17.58 becomes a few dollars a month instead. See Pause it for free, below.

What you pay on RAD​

In a Google Cloud project you ownIn a project RAD manages for you
Module fee, once per deployment75 credits (US$7.50 at the top-up price)67.5 credits (10% lower)
Build timeA few credits per build (roughly 3–6)The same
Google Cloud running costBilled by Google to your own billing account, about US$17.58 a month for the default configuration (table below)Metered hourly in credits; RAD publishes 3 credits a day, about 90 a month (about US$9 at the top-up price)
Who owns whatYou own the project and its billing; RAD deploys and updates itRAD owns the project, with guardrails, quotas and budget alerts; you get console access, and a minimum purchased balance is held in reserve

10 credits cost US$1 on a one-off top-up, less on a plan. In a project RAD manages, the database, file server and network are shared by every application in the project, so a second application does not add a second database.

Default running cost in your own project (us-central1, Google list prices):

Resource (module default)Per month
Cloud Run, 1x vCPU / 1 GiB (scaleToZero)US$11.58
Cloud NAT and networkingUS$5
Cloud Storage (add-ons, backups)US$1
Totalabout US$17.58

How it compares​

  • There is no comparable managed hosting offer to point to here; the honest comparison is what is managed for you — backups, the sandboxed execution environment, Secret Manager and monitoring — rather than a sticker price, since the module runs serverless and scales to zero between requests.
  • RAD never claims to be cheaper than bare infrastructure in cash terms — it usually is not. The difference is what RAD manages for you: patching, backups, secrets and monitoring, plus the ability to pause DataAnalyst for free rather than paying for idle capacity (see below).

Pause it for free: delete a RAD-managed project, restore it when you need it​

If DataAnalyst runs in a project RAD manages for you, you have a second option that goes well beyond scaling to zero: delete the whole project, and restore it within 30 days for close to nothing. This suits DataAnalyst that you only need occasionally far better than running it continuously.

  • How it works. Deleting a RAD-managed project unlinks its billing first, then asks Google to delete the project. Google does not remove the project immediately: it keeps it, recoverable, for 30 days. Because billing is already unlinked, nothing is charged while it waits. Unlike deleting one module, this does not tear down Cloud Run resources one by one — the whole project, and everything in it, simply stops.
  • Restoring costs a handful of credits, not a rebuild. Within 30 days, the project's owner can restore it. RAD asks Google to undelete the project and reattaches its billing account, then asks you to run Update on each deployment to confirm everything came back. Because nothing was individually destroyed, that Update finds the same resources already there — it is a check, not a rebuild, and an Update never charges the module fee again. That is a handful of credits (under US$1) in total for a typical DataAnalyst chain, against the 80 credits a full redeploy costs.
  • So a month of occasional use can cost a few dollars, not US$17.58. Deploy DataAnalyst, use it, delete the project. Restore it next time you want it, confirm with Update, and delete it again when you're done. You pay only for the module fee once, the builds, and whatever hours it was actually live.
  • What this needs. You must own the project (not a bring-your-own one RAD only manages billing for), and you restore it yourself within the 30 days — after that, Google deletes it for good. Restoring is admitted like creating a new project: your purchased credit balance must still clear the tier's floor (100 credits for the sandbox tier most study and demo use fits). Google says most services are fully working again within 36 hours of a restore.
  • One real gap: the backup bucket does not survive. Nightly backups are written to a bucket inside the project, and that bucket is not protected by Cloud Storage's soft-delete, so it is very likely gone as soon as you delete the project — even though the project itself is recoverable for 30 days. If you have customised DataAnalyst and want to keep that work, copy a backup out (to Google Drive, or a bucket outside the project) before you delete. For a default installation with nothing irreplaceable in it, this does not matter.

Pay only while you use it, the other way: delete and redeploy​

Cloud Run already scales to zero between requests. The option above only applies to a RAD-managed project; in your own project, or once the 30-day window has passed, the way to stop paying is to delete the deployment and deploy it again when you need it.

  • What a redeploy costs. The module fee again plus the builds: about 80 credits (US$8) in your own project. Deleting saves money only once DataAnalyst would otherwise sit unused for about 14 days or more in your own project (US$0.59 a day), or about 25 days or more in a RAD-managed one (3 credits a day).
  • Keep your data first. Nightly backups are written to a bucket inside the deployment and are deleted with it, so copy the latest backup out before deleting if you want to keep it.
  • Note your settings. RAD does not recreate a deleted deployment for you; you enter the settings again when you deploy.

Lab sessions and Managed Environments​

  • Lab sessions, for training. A trainer runs a session for a class. Each participant gets the app in their own Google Cloud project for 15 minutes to 24 hours, within an allowance the trainer sets. Either the trainer funds every place, or each participant pays for their own. Everything is deleted when the session ends and unused credits go back to the trainer.
  • Managed Environments, for consultancies. A partner runs the app for a client from a ring-fenced wallet it funds, and settles with the client directly. If the wallet runs low, billing pauses and the data is kept, so nobody receives an unexpected charge. At the end the partner hands the project over and the deployments become the client's own.

Credits can be bought in more than 20 currencies, including XAF, XOF, NGN, GHS, KES and ZAR, by card, bank transfer or mobile money.

Sources (8 October 2026): Google Cloud list prices from the Cloud Billing Catalog API; RAD's own fees and daily-credit estimates from radmodules.dev/pricing; Delete and restore projects for the 30-day recovery window and what it says about Cloud Storage objects without soft delete. Prices change; check each source before relying on a figure.

1. Overview​

CapabilityGoogle Cloud serviceNotes
ComputeCloud Run v2Python (FastAPI + Google ADK) service; request-based autoscaling by default
AI modelVertex AI (Gemini)No API key secret to manage — uses the Cloud Run service's own runtime service account, granted roles/aiplatform.user
Code executionCloud Run's sandbox launchergVisor-isolated: deny-by-default network egress, writes discarded after each call, no access to this service's own environment variables
Uploaded dataEphemeral container storageNo database and no GCS bucket at all — a file lives only under /tmp/uploads/<session>/ for the life of a chat session
NetworkingVPC / Direct VPC EgressDeployable standalone (inline VPC) or converged onto a shared Services_GCP network when one exists in the project
IngressCloud Run URLDefault run.app URL; Identity-Aware Proxy strongly recommended (see below)

Sensible defaults worth knowing up front:

  • No backend credential. The agent has no API key and no database password — Vertex AI authentication is entirely the Cloud Run service's own runtime identity. The one Secret Manager secret it can hold is the visitor access key described below.
  • public_access defaults to true. Unlike most catalogue modules, this one has no proprietary reference material to protect (it started life as a reference-catalogue chat advisor and was later re-purposed into a general-purpose sandboxed code-execution agent — see DataAnalyst_Common for that history), so there is no confidentiality reason to restrict who can self-serve deploy it.
  • The sandbox launcher IS the security boundary, not a convenience. Every code-execution/file-preview tool call runs through it. Disabling enable_sandbox_launcher removes process isolation entirely — it is not a performance knob.
  • Every execute_code run has its own memory/CPU ceiling (execute_code_memory_limit_mb, default 512 MiB), independent of and below the container's overall memory_limit — a runaway allocation fails cleanly with MemoryError rather than threatening the whole instance.
  • Charts are delivered by the server, not trusted to the model. The agent can generate a matplotlib chart; the image is extracted directly from the tool-call response and pushed to the browser as its own message — never relied on to survive intact inside the model's own natural-language reply.
  • No hard isolation between concurrent chat sessions on one warm instance. Session directories are named with an unguessable UUID, but that is a soft boundary, not an OS-level one — see §3 and §6.
  • min_instance_count defaults to 0 (scale-to-zero); cold start is fast since there is no repository to clone and no database connection to establish.

2. Google Cloud Services & How to Explore Them​

All commands assume PROJECT and REGION are set. Service and resource names are reported in the deployment Outputs.

A. Cloud Run — the agent service​

The agent runs as a Cloud Run v2 service. Each deployment creates an immutable revision.

  • Console: Cloud Run → select the service for revisions, traffic, logs, and metrics.
  • CLI:
    gcloud run services list --project "$PROJECT" --region "$REGION"
    gcloud run services describe <service-name> --project "$PROJECT" --region "$REGION"
    gcloud run revisions list --service <service-name> --project "$PROJECT" --region "$REGION"

See App_CloudRun for scaling, concurrency, execution environment, and traffic splitting. Note max_concurrent_requests is applied via a gcloud beta run services update escape hatch after the service is created (App_CloudRun has no first-class concurrency override) — confirm it took effect:

gcloud run services describe <service-name> --region "$REGION" \
--format='value(spec.template.spec.containerConcurrency)'

B. Vertex AI — the model backend​

The agent calls Gemini via Vertex AI using the Cloud Run service's own runtime service account — there is no API key to create, store, or rotate.

  • Console: Vertex AI → Model Garden, or check IAM for the roles/aiplatform.user grant on the runtime service account.
  • CLI:
    gcloud projects get-iam-policy "$PROJECT" \
    --flatten="bindings[].members" \
    --filter="bindings.role:roles/aiplatform.user"

C. Cloud Run's sandbox launcher — code execution isolation​

Not a resource you browse in the Console, but the central mechanism this module exists to use. Applied post-deploy via a gcloud beta escape hatch (--sandbox-launcher, no first-class Terraform field exists for it yet — see enable_sandbox_launcher).

  • Confirm it's applied:
    gcloud run services describe <service-name> --region "$REGION" \
    --format='value(metadata.annotations)'
  • Confirm it's actually isolating real calls — look for [start]/[end] exit_code=... lines in Cloud Logging around a real chat interaction:
    gcloud logging read 'resource.type="cloud_run_revision"
    resource.labels.service_name="<service-name>" textPayload:"sandbox"' \
    --project "$PROJECT" --freshness=10m --order=asc

Cloud Run sandboxes are a Public Preview feature — the exact invocation contract and availability are subject to change without GA-level stability guarantees.

D. Networking & ingress​

The service is reachable at its run.app URL by default. If the project already has a Services_GCP deployment, this module converges onto its shared VPC instead of maintaining its own inline network — see §6 for what that means during an update.

  • Console: Cloud Run (service URL); VPC network → VPC networks / Subnets.
  • CLI:
    gcloud run services describe <service-name> --region "$REGION" --format='value(status.url)'
    gcloud compute networks list --project "$PROJECT"

See App_CloudRun for ingress settings, VPC egress, and load balancing.

E. Cloud Logging & Monitoring — structured tool-call logs​

Every list_uploaded_files/inspect_file/execute_code call emits one JSON line (tool name, session ID, success/failure, duration, exit code) — Cloud Logging parses this automatically into jsonPayload fields and severity, with no logging library or added dependency involved.

  • Console: Logging → Logs Explorer.
  • CLI — query by field, not raw text:
    # every failed execute_code call in the last hour
    gcloud logging read 'resource.type="cloud_run_revision"
    resource.labels.service_name="<service-name>"
    jsonPayload.tool="execute_code" jsonPayload.ok=false' \
    --project "$PROJECT" --freshness=1h

    # a specific session's activity
    gcloud logging read 'resource.type="cloud_run_revision"
    resource.labels.service_name="<service-name>"
    jsonPayload.session_id="<session-id>"' \
    --project "$PROJECT" --freshness=1h --order=asc

3. Application Behaviour​

  • Upload-then-ask workflow, multiple files at once. The chat page's upload control accepts several files in one selection (uploaded sequentially, one POST /upload per file), so cross-file analysis (e.g. joining two CSVs) needs no separate "attach" step in the chat protocol. Files are validated by extension (allowed_upload_extensions) and a streamed size cap (max_upload_size_mb) before a single byte touches disk.
  • Session-scoped, ephemeral storage. Uploaded files live under /tmp/uploads/<session_id>/ for the life of a chat session — swept opportunistically the next time anyone uploads to the same instance, once older than upload_ttl_seconds. A cold start or a different instance starts with nothing uploaded.
  • The agent computes; it does not guess. Every non-trivial question is answered by writing and running real code (execute_code), never by reasoning from a file preview alone. If the code errors, the agent reads the traceback, fixes it, and retries — visible directly in the structured logs (§2E) as a failed call followed by a successful one.
  • Chart generation, delivered reliably. The agent can render a matplotlib chart (Agg backend, no display needed) by printing a CHART_PNG_BASE64:<base64> marker line from inside the sandbox; the server extracts it from the raw tool-call response and pushes it to the browser as its own image message — independent of what the model's own text reply says, since an LLM is not reliably good at re-quoting a multi-hundred-KB blob verbatim.
  • Excel support. .xlsx files are accepted alongside CSV/TSV/JSON/plain text (openpyxl backs pandas' Excel engine).
  • Structure-aware preview for JSON. Rather than a raw byte truncation that could cut a JSON file mid-structure, the agent's file preview returns the first few list items or dict keys, pretty-printed, falling back to a raw preview only if parsing fails.
  • Per-call resource ceiling. Each execute_code run applies its own memory (RLIMIT_AS) and CPU-time (RLIMIT_CPU) limit before the generated code executes, bounded by execute_code_memory_limit_mb and the call's own timeout — independent of, and below, the container's overall memory_limit.
  • Abuse/cost guards. A per-connection message-length cap and rate limit, plus a per-instance cap on messages per minute across every connection (INSTANCE_MAX_MESSAGES_PER_MINUTE, default 120, 0 to disable), bound how much Vertex AI billing (and how many execute_code runs) one caller can drive. The per-connection limit alone is defeated by opening another connection; the per-instance one is not.
  • An access key on a RAD-managed project. With access_control = "auto" (default), a RAD-managed project (billed to RAD) requires a per-deployment access key whenever the service is publicly reachable (no enable_iap, ingress not internal); a project you own is left unchanged. Visitors see a sign-in box; after the key is entered once the server sets a signed, HttpOnly, Secure, SameSite=Strict session cookie (12 hours). Until then /upload and /ws/chat answer 401, and a misconfigured service with no usable key answers 503 — it fails closed. Leave access_key empty and a 32-character key is generated and shown as the access_key output (owner and administrators only); set your own (16+ characters) to keep it off the Outputs tab. Changing it rolls a new revision and signs everyone out. The key is shared, not per-user. A deployer cannot turn it off on a RAD-managed project; only an administrator can. Scripts send it as an X-Access-Key header, or POST /auth with {"key": "..."}; /docs, /redoc and /openapi.json are not served.
  • An open service on a RAD-managed project is scale-bounded. IAP cannot be the default (it needs an OAuth consent screen in the project, which a freshly created RAD-managed project does not have and can no longer be given by API), so while enable_iap = false and ingress_settings = "all", a RAD-managed project (billed to RAD) is held to max_instance_count <= 2 and max_concurrent_requests <= 10 — the module defaults — and the plan refuses larger values. Behind IAP, with ingress_settings = "internal", or in your own project, there is no such limit.
  • Not available in a RAD-managed sandbox or lab project. Vertex AI is not permitted in those tiers, so the plan refuses the deployment up front with a message naming the way out (a development or production RAD-managed project, or your own GCP project).

4. Configuration Variables​

Variables are grouped exactly as they appear on the deployment platform. Only settings specific to or notable for DataAnalyst_CloudRun are listed; every other input is inherited from App_CloudRun with its standard behaviour.

Group 1 — Project & Identity​

VariableDefaultDescription
project_id(required)Target Google Cloud project.
regionus-central1Region for the service and regional resources.

Group 2 — Deployment Environment​

VariableDefaultDescription
tenant_iddemoShort suffix that makes resource names unique per environment.
support_users[]Emails granted project access and monitoring alerts.
resource_labels{}Labels applied to all resources.

Group 3 — Application Identity​

VariableDefaultDescription
application_namedataanalystBase name for the Cloud Run service and Artifact Registry repository.
application_display_nameData Analyst AgentFriendly name shown in the Console.
application_description(set)Service description.
application_version1.1.0Container image version tag — bump on every code-only redeploy, since the image is referenced by a mutable tag, not a digest.

Group 4 — Runtime & Scaling​

VariableDefaultDescription
deploy_applicationtrueSet false to provision infrastructure only.
cpu_limit1000mCPU per instance.
memory_limit1GiMemory per instance — headroom above the platform's usual 512Mi floor, since pandas/numpy loading an uploaded file benefits from it.
min_instance_count0Minimum instances (0 = scale-to-zero).
max_instance_count2Maximum instances.
execution_environmentgen2Required by the sandbox launcher.
container_protocolhttp1Plain HTTP/1.1 + WebSocket.
timeout_seconds600Kept generous since the chat WebSocket holds this open across a turn.

Group 5 — Access & Ingress Control​

VariableDefaultDescription
ingress_settingsallWhich networks may reach the service.
vpc_egress_settingPRIVATE_RANGES_ONLYHow outbound traffic is routed through the VPC.
enable_iapfalseEvery message here is a real Vertex AI call AND can trigger code execution. Not the default because IAP needs an OAuth consent screen in the project; while it is off on a RAD-managed project, an access key is required and scale is bounded (see above).
access_controlautoauto: an access key is required on a RAD-managed project while the service is publicly reachable, unchanged on your own project; required: always; off: never (refused on a RAD-managed project).
access_key""Your own key (16+ characters). Empty generates one, shown as the access_key output. Change it to rotate.
iap_authorized_users / iap_authorized_groups[]Who may access through IAP.

Group 6 — Environment Variables & Secrets​

VariableDefaultDescription
environment_variables{}Extra non-secret settings.
secret_environment_variables{}Not used by this module by default — the agent has no secret of its own.

Group 8 — CI/CD & Binary Authorization​

Standard App_CloudRun Cloud Build / Cloud Deploy integration — see App_CloudRun. Key input specific to this module:

VariableDefaultDescription
additional_cloudrun_sa_roles["roles/aiplatform.user"]Extra IAM roles for the Cloud Run service account. Must keep roles/aiplatform.user — removing it breaks every chat message with a Vertex AI permission error.

Group 10 — Domain, CDN & Cloud Armor​

Standard App_CloudRun load-balancer options (application_domains, enable_cdn, enable_cloud_armor, admin_ip_ranges) — see App_CloudRun.

Group 14 — Observability & Health​

VariableDefaultDescription
startup_probe / liveness_probeHTTP GET /_healthMatches the FastAPI server's health route. Not named /healthz — confirmed live that Cloud Run's edge routing treats that exact path as reserved and never reaches the container.
uptime_check_configdisabledCloud Monitoring uptime check.

Group 15 — Data Analyst Agent Configuration​

VariableDefaultDescription
vertex_regionus-central1Vertex AI region serving the agent's Gemini calls.
agent_modelgemini-2.5-flashGemini model ID used via Vertex AI.
enable_sandbox_launchertrueThe actual security boundary for code execution — see §1.
max_upload_size_mb20Enforced by streaming the upload and rejecting once this many bytes have arrived — never trusted from the request's declared size.
allowed_upload_extensions[".csv", ".tsv", ".json", ".txt", ".xlsx"]Extensions the upload endpoint accepts.
upload_ttl_seconds3600How long an uploaded file is kept before cleanup eligibility.
execute_code_memory_limit_mb512Per-execute_code-call memory ceiling — keep comfortably below memory_limit.
max_concurrent_requests10Cap on concurrent WebSocket connections per instance — also the knob to set to 1 if the cross-session isolation caveat (§6) matters for your data.
require_services_gcp_modulefalseThis module has no database/NFS/GKE dependency of its own, so it deploys standalone into a blank project by default.

Group 22 — VPC Service Controls & Audit Logging​

Standard App_CloudRun options (enable_vpc_sc, vpc_cidr_ranges, vpc_sc_dry_run, organization_id, enable_audit_logging) — see App_CloudRun.


5. Outputs​

OutputDescription
service_nameCloud Run service name.
service_urlDefault run.app URL — open this to chat with the agent.
access_key_requiredWhether the agent demands an access key.
access_keyThe generated access key to enter on the sign-in screen (empty when you chose your own key or none is required).
service_locationRegion the service runs in.
sandbox_launcher_enabledWhether --sandbox-launcher was applied.
container_image / container_registryDeployed image and Artifact Registry repo.
network_name / network_existsVPC network in use, and whether it was discovered (shared) rather than created inline.
storage_bucketsAlways empty — this module provisions no GCS bucket.
monitoring_enabledMonitoring status.
deployment_id / tenant_id / resource_prefixNaming identifiers.
project_id / project_numberProject identifiers.

6. Configuration Pitfalls & Sensible Defaults​

Risk: Critical (data loss / outage / security) — High (service degraded) — Medium (cost or partial degradation) — Low (minor).

SettingSensible valueRiskConsequence if wrong
enable_sandbox_launchertrueCriticalfalse removes the ONLY isolation between LLM-generated code and the rest of the container — the module's entire security model depends on this staying on.
enable_iap / access_controlIAP, or an access key, for anything beyond a quick testHighOn your own project with access_control = "auto" and IAP off, the service is open: any caller can drive Vertex AI billing and code-execution runs, bounded only by the rate limits. Set access_control = "required" or enable IAP.
execute_code_memory_limit_mbcomfortably below memory_limitHighSet too close to (or above) memory_limit, a single pathological allocation can still threaten the whole instance instead of failing cleanly with MemoryError.
max_concurrent_requests1 for genuinely sensitive dataMediumSession directories are isolated only by an unguessable name, not an OS-level permission boundary — concurrent sessions on one warm instance can, in principle, read each other's uploaded files.
application_versionbump on every code-only changeMediumThe image is referenced by a mutable tag; rebuilding under the same tag creates no new revision, so a "redeploy" silently keeps serving the old container.
allowed_upload_extensionskeep to data formats onlyMediumWidening this to arbitrary file types expands what a sandboxed-but-real Python process can be asked to parse.
upload_ttl_secondsshorter for public-facing deploymentsLowA long TTL keeps a stranger's uploaded data on the instance's disk longer than necessary if the browser tab is simply abandoned.

For the foundation behaviour referenced throughout — service identity, scaling and concurrency, ingress and load balancing, CI/CD, Cloud Armor, IAP, Binary Authorization, VPC-SC, backups, and image mirroring — see App_CloudRun. The shared application configuration (container build, upload handling, and the sandboxed code-execution model) is described in DataAnalyst_Common.

Need RAD to do something it does not do yet? Request it on the roadmap, or vote on what is already there.