Skip to main content

Element on Google Cloud Run

Element on Google Cloud Run

Element is the leading open-source (AGPLv3) Matrix web client — a self-hosted, end-to-end-encrypted messaging and collaboration app. This module deploys Element on Cloud Run v2 on top of the App_CloudRun foundation, which provisions and manages the shared Google Cloud infrastructure.

Element is a static nginx single-page application (SPA): the browser talks directly to a Matrix homeserver (such as Synapse or Dendrite) over HTTPS, so the container itself holds no server-side state — no database, no Redis, no persistent storage, and no secrets.

This guide focuses on the cloud services Element 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, and the deployment lifecycle — refer to the App_CloudRun foundation guide rather than repeating them here.


What Element costs on RAD, and how that compares​

Element on RAD's Cloud Run module costs about US$16.78 a month. A self-managed server is cheaper in cash, but then you run the OS, security patches and upgrades yourself. GKE Autopilot suits Element that must stay up continuously or scale across pods; for the lowest cost, this Cloud Run module already gives you the cheaper option — see the GKE guide if you need the other one. Figures are as at 8 October 2026; sources are listed at the end of this section.

If you only need it occasionally — studying, a demo, a seasonal business — a RAD-managed project can be deleted and restored within 30 days for a handful of credits, so that US$16.78 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 deployment40 credits (US$4 at the top-up price)36 credits (10% lower)
Build timeA few credits per buildThe same
Google Cloud running costBilled by Google to your own billing account, about US$16.78 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, and less on a monthly 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 / 0.5 GiB (scaleToZero)US$10.78
Cloud NAT and networkingUS$5
Cloud Storage (add-ons, backups)US$1
Totalabout US$16.78

How it compares​

  • Element's own paid tiers (Enterprise, Sovereign) are priced per seat or per deployment with no published rate, so there is nothing to cite here. Element is also unusually light to self-host, since it is a static single-page app with no database of its own — even the smallest, cheapest VPS tier comfortably serves it, well under the ~US$24/month 2 vCPU class a heavier app needs.
  • The real cost of running Matrix is almost entirely the homeserver (Synapse or Dendrite) that Element connects to, which is a separate deployment this module does not include.

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

If Element 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 Element you only need occasionally — studying, a demo, a seasonal business — 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 or its networking 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 costs a handful of credits (under US$1) in total for a typical Element-sized deployment chain, against the 40 credits (US$4.05) a full redeploy costs.
  • So a month of occasional use can cost a few dollars, not US$16.78. Deploy Element, use it for a while, 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 Element 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.
  • The one real gap elsewhere does not apply here. Element keeps no data of its own — no database, no storage bucket — so there is nothing for the restore to lose. Your Matrix conversation history lives on the homeserver you pointed Element at, not in this deployment.

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 44 credits (US$4.45), and about an hour, because RAD recreates the project and shared services before Element. Deleting saves money once Element would otherwise sit unused for about 8 days or more in your own project (about US$0.56 a day), or about 14 days or more in a RAD-managed one (3 credits a day).
  • Delete everything Element uses. Element holds no server-side state at all — no database, no file storage — so deleting it stops all of its own running cost right away, and there is nothing it shares with other applications in the project to leave running.
  • Keep your data first. Element itself stores nothing to back up; it is a stateless client pointed at a Matrix homeserver you run separately, so deleting the deployment loses only its own configuration (ingress rules, domains), not any chat history or user data.
  • 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 Element 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 Element 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 pause/restore mechanics and window.

1. Overview​

Element runs as a static nginx container on Cloud Run v2. The deployment wires together a deliberately small set of Google Cloud services:

CapabilityGoogle Cloud serviceNotes
ComputeCloud Run v2nginx static SPA, 1 vCPU / 512 MiB by default, serverless autoscaling; scale-to-zero enabled
Container buildCloud Build + Artifact RegistryThin custom image FROM vectorim/element-web with a runtime config.json entrypoint
IngressCloud Run URL / Cloud Load BalancingDefault run.app URL; optional external HTTPS load balancer + custom domain
Secrets—None. Element requires no secrets
Database—None. The Matrix homeserver holds all state, not Element
Object storage—None. Element is stateless

Sensible defaults worth knowing up front:

  • Element is stateless. All chat state, encryption keys, and media live on the Matrix homeserver and in the user's browser. Element itself stores nothing server-side, so there is no database, Redis, GCS bucket, or Secret Manager secret.
  • The homeserver is runtime configuration. homeserver_url / homeserver_name are written into /app/config.json by the container entrypoint on every start, so one image can point at any homeserver without a rebuild. Leaving them blank defaults to the public matrix.org.
  • Custom build with a pinned version. container_image_source = "custom" builds a thin image over vectorim/element-web. application_version = "latest" resolves to the pinned known-good tag v1.11.86 via an app-specific ELEMENT_VERSION build ARG.
  • Scale-to-zero is enabled by default (min_instance_count = 0, cpu_always_allocated = false). A static asset server costs nothing at idle and cold-starts in well under a second — cold starts are a non-issue for Element.
  • Public ingress by default. ingress_settings = "all" makes the client UI reachable; Element performs its own login against the homeserver. Add IAP if you want a Google-identity gate in front of the UI.
  • Port 80. nginx serves the SPA on port 80; probes target /.

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 Element service​

Element runs as a Cloud Run v2 service that autoscales by request load between the minimum and maximum instance counts. Each deployment creates an immutable revision; traffic can be split across revisions for safe rollouts.

  • 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.

B. Container image — Cloud Build & Artifact Registry​

The Element image is built by Cloud Build from a thin Dockerfile that layers a config.json-generating entrypoint on top of vectorim/element-web, then pushed to Artifact Registry. application_version = "latest" builds the pinned v1.11.86.

  • Console: Cloud Build → History; Artifact Registry → Repositories.
  • CLI:
    gcloud builds list --project "$PROJECT" --limit 5
    gcloud artifacts docker images list <region>-docker.pkg.dev/<project>/<repo> --project "$PROJECT"

See App_CloudRun for the build pipeline, image mirroring, and retention policy.

C. Networking & ingress​

The service is reachable at its run.app URL by default. An external HTTPS load balancer with a custom domain, Cloud CDN, and Cloud Armor can be layered on; ingress settings and VPC egress control connectivity. Because Element is a static asset server, it is a strong candidate for Cloud CDN.

  • Console: Cloud Run (service URL); Network services → Load balancing.
  • CLI:
    gcloud run services describe <service-name> --region "$REGION" --format='value(status.url)'
    gcloud compute addresses list --project "$PROJECT"

See App_CloudRun.

D. Identity-Aware Proxy (optional)​

Element ships open by default so users can log in against the homeserver. To restrict who can even load the client UI to your Google identities, enable IAP (enable_iap = true).

  • Console: Security → Identity-Aware Proxy.
  • CLI:
    gcloud iap web get-iam-policy --resource-type=backend-services --project "$PROJECT"

See App_CloudRun for IAP setup and the OAuth consent screen.

E. Cloud Logging & Monitoring​

Container (nginx access/error) logs flow to Cloud Logging; Cloud Run metrics flow to Cloud Monitoring, with an uptime check and check-failure alert provisioned when the endpoint is publicly reachable.

  • Console: Logging → Logs Explorer; Monitoring → Dashboards / Alerting / Uptime checks.
  • CLI:
    gcloud run services logs read <service-name> --project "$PROJECT" --region "$REGION" --limit 50

3. Element Application Behaviour​

  • Runtime config generation. The container entrypoint writes /app/config.json on every start from HOMESERVER_URL / HOMESERVER_NAME, then hands off to nginx. Changing the homeserver is a redeploy with new env values — no image rebuild.
  • No database, no migrations. Element serves static assets; there is no schema, no init job, and no first-boot migration window. The service is Ready as soon as nginx binds port 80.
  • Login is a browser-to-homeserver flow. Element authenticates the user directly against the configured Matrix homeserver; there is no server-side session in the Cloud Run container and nothing to seed in Secret Manager.
  • Verify the injected homeserver. Confirm the running revision's env matches your intended homeserver:
    gcloud run services describe <service-name> \
    --region "$REGION" --project "$PROJECT" \
    --format='value(spec.template.spec.containers[0].env)'
    Then open $SERVICE_URL — the login screen should show your homeserver, and curl -s "$SERVICE_URL/config.json" should return the JSON with your base_url.
  • Health path. Startup and liveness probes target /, which nginx answers immediately and unauthenticated.
  • Upgrading Element. Bump application_version (or pin a newer element-web tag) and redeploy; a new image builds and a new revision rolls out. Because Element reuses a version tag across rebuilds, the build's content-hash trigger drives the new image; verify the deployed revision's digest if a change seems stale.

4. Configuration Variables​

Variables are grouped exactly as they appear on the deployment platform. Only settings specific to or notable for Element 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_nameelementBase name for resources. Do not change after first deploy.
application_display_nameElementHuman-readable name shown in the Console. Not customised for Element in variables.tf — override with e.g. "Element" for a clearer display name.
application_versionlatestElement image tag; latest builds the pinned v1.11.86. Pin a specific element-web tag in production.
homeserver_url""Matrix homeserver base URL written into config.json. Blank → matrix.org.
homeserver_name""Matrix server name (delegation identity) advertised by Element. Blank → matrix.org.

Group 4 — Runtime & Scaling​

VariableDefaultDescription
deploy_applicationtrueSet false to provision infrastructure only.
container_image_sourcecustomBuilds the thin Element image via Cloud Build.
container_image""Override with a prebuilt/mirrored image URI.
cpu_limit1000mCPU per instance.
memory_limit512MiMemory per instance (gen2 floor is 512 MiB).
container_port80nginx listens on port 80.
min_instance_count0Hardcoded, not tunable. element.tf pins this to 0 in the Foundation module call and the config merge; var.min_instance_count is never forwarded, so raising it (e.g. to eliminate cold starts) is silently ignored. Scale-to-zero is always in effect — a static server is free at idle regardless of the input value.
max_instance_count3Autoscaling upper bound — genuinely adjustable; forwarded via var.max_instance_count.
cpu_always_allocatedfalseRequest-based billing (cheaper) — Element does no background work.
execution_environmentgen2Cloud Run execution environment.
enable_cloudsql_volumefalseNo database — Auth Proxy not mounted.
enable_image_mirroringtrueMirror the image into Artifact Registry.

Group 5 — Access & Ingress Control​

VariableDefaultDescription
ingress_settingsallPublic UI. Set internal to restrict to the VPC.
vpc_egress_settingPRIVATE_RANGES_ONLYRoute only RFC 1918 traffic via VPC.
enable_iapfalseRequire Google sign-in to load the client UI.
iap_authorized_users / iap_authorized_groups[]Who may access through IAP.

Group 6 — Environment Variables & Secrets​

VariableDefaultDescription
environment_variables{}Extra settings merged with the injected HOMESERVER_URL / HOMESERVER_NAME.
secret_environment_variables{}Secret Manager references. Element needs none.
secret_propagation_delay30Seconds to wait after secret creation.
secret_rotation_period2592000sRotation notification frequency.

Group 7 — Backup & Restore​

Inherited from App_CloudRun and no-ops for Element (nothing to back up). backup_schedule, backup_retention_days, enable_backup_import, backup_source, backup_uri, backup_format.

Group 8 — CI/CD & Binary Authorization​

Standard App_CloudRun Cloud Build / Cloud Deploy integration — see App_CloudRun. Key inputs: enable_cicd_trigger, github_repository_url, github_token, enable_cloud_deploy, enable_binary_authorization.

Group 9 — Load Balancer, CDN & Image Retention​

VariableDefaultDescription
enable_cloud_armorfalseProvision Global HTTPS LB + Cloud Armor WAF.
admin_ip_ranges[]CIDR ranges exempted from WAF rules.
application_domains[]Custom domain names for the HTTPS LB.
enable_cdnfalseEnable Cloud CDN — worthwhile for Element's static assets.
max_images_to_retain / delete_untagged_images / image_retention_days(set)Artifact Registry cleanup policy.

Group 11 — Storage & Filesystem​

Inherited and not used by Element (stateless). create_cloud_storage, storage_buckets (empty), enable_nfs (false), gcs_volumes (empty), manage_storage_kms_iam, enable_artifact_registry_cmek.

Group 12 — Database​

Inherited from App_CloudRun and inert — Element_Common sets database_type = "NONE". No Cloud SQL instance, user, or password is created.

Group 13 — Jobs & Scheduled Tasks​

VariableDefaultDescription
initialization_jobs[]Element declares no init jobs.
cron_jobs[]No platform-scheduled recurring tasks.

Group 14 — Observability & Health​

VariableDefaultDescription
startup_probeHTTP / 10 s delay, 6 failuresStartup probe.
liveness_probeHTTP / 15 s delayLiveness probe.
uptime_check_config{ enabled = false, path = "/" }Optional Cloud Monitoring uptime check (endpoint is public).
alert_policies[]Metric alert policies.

Group 21 — Redis Cache & Queue​

Inherited from App_CloudRun and inert — a static SPA has no server-side cache or queue. enable_redis, redis_host, redis_port, redis_auth.

Group 22 — VPC Service Controls & Audit Logging​

VariableDefaultDescription
enable_vpc_scfalseEnforce a VPC-SC perimeter (requires organization_id).
vpc_cidr_ranges / vpc_sc_dry_run(set)Access level CIDRs / dry-run mode.
enable_audit_loggingfalseDetailed Cloud Audit Logs.

5. Outputs​

Returned on a successful deployment — the quickest way to locate and explore the running resources.

OutputDescription
service_nameCloud Run service name.
service_urlDefault run.app URL of the service.
service_locationRegion the service runs in.
stage_servicesStage-specific service URLs (Cloud Deploy).
load_balancer_ip / load_balancer_urlExternal HTTPS load balancer IP / URL (when enabled).
storage_bucketsCreated Cloud Storage buckets (empty for Element).
network_name / network_exists / regionsVPC network, presence, regions.
container_image / container_registryDeployed image and Artifact Registry repo.
monitoring_enabled / monitoring_notification_channels / uptime_check_namesMonitoring status, channels, uptime checks.
initialization_jobsNames of setup jobs (none for Element).
deployment_id / tenant_id / resource_prefixNaming identifiers.
project_id / project_numberProject identifiers.
cicd_enabled / github_repository_url / github_repository_owner / github_repository_name / cicd_configurationCI/CD status and details.
artifact_registry_repository / cloudbuild_trigger_name / cloudbuild_trigger_idRegistry and build trigger.
vpc_sc_enabled / vpc_sc_perimeter_name / vpc_sc_dry_run_modeVPC-SC status.
audit_logging_enabled / artifact_registry_cmek_enabledAudit logging and CMEK status.

6. Configuration Pitfalls & Sensible Defaults​

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

Inherited plan-time validation. This module passes its configuration through the App_CloudRun foundation engine, which validates values and combinations at plan time — IAP with no authorized identities, a gen1 runtime with NFS/GCS mounts, an out-of-range timeout_seconds. Invalid configuration fails the plan with a clear, named error before any resource is created.

SettingSensible valueRiskConsequence if wrong
homeserver_url / homeserver_nameYour real homeserver, or blank for matrix.orgHighA wrong or unreachable homeserver leaves users unable to log in — the UI loads but authentication fails.
application_versionPin a real element-web tagHighlatest is not a valid element-web tag; the module pins v1.11.86, but a hand-set latest in a raw build ARG would fail with MANIFEST_UNKNOWN.
ingress_settingsall for public UIHighinternal makes the client UI unreachable from browsers outside the VPC.
container_image_sourcecustomHighSwitching to prebuilt with an image that lacks the config.json entrypoint ships Element pointed at the wrong homeserver (or none).
memory_limit512MiMediumThe gen2 execution environment rejects anything below 512 MiB at plan time, regardless of billing mode.
enable_iapEnable to gate the UIMediumWithout IAP anyone with the URL can load the client (they still need homeserver credentials to log in).
enable_cdnEnable for public deploymentsLowServing static assets straight from Cloud Run misses an easy latency/egress win.
Database / Redis / Backup inputsLeave defaultLowInert for Element; setting them has no effect.

For the foundation behaviour referenced throughout — service identity, scaling and concurrency, ingress and load balancing, CI/CD, Cloud Armor, IAP, Binary Authorization, VPC-SC, and image mirroring — see App_CloudRun. Element-specific application configuration shared with the GKE variant is described in Element_Common.

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