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.


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
SecretsNone. Element requires no secrets
DatabaseNone. The Matrix homeserver holds all state, not Element
Object storageNone. 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_deployment_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_nameApp_CloudRun ApplicationHuman-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_count0Scale-to-zero — a static server is free at idle.
max_instance_count3Autoscaling upper bound.
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 inertElement_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.