Certification track: Associate Cloud Engineer (ACE)
Umami on Google Cloud Run
This document provides a comprehensive reference for the modules/Umami_CloudRun Terraform module. It covers architecture, IAM, configuration variables, Umami-specific behaviours, and operational patterns for deploying Umami on Google Cloud Run (v2).
1. Module Overview
Umami is a privacy-focused, lightweight, open-source web analytics platform — a self-hosted alternative to Google Analytics. It collects website traffic and event data without cookies or personal data, making it GDPR, CCPA, and PECR compliant out of the box. Umami CloudRun is a wrapper module built on top of App CloudRun. It uses App CloudRun for all GCP infrastructure provisioning and injects Umami-specific application configuration, database initialisation, and secret management via Umami Common.
Key Capabilities:
- Compute: Cloud Run v2 (Gen2), Next.js container, 1 vCPU / 512Mi by default. Scale-to-zero (
min_instance_count = 0) supported;max_instance_countdefaults to 3. - Data Persistence: Cloud SQL PostgreSQL 15. Umami is a stateless analytics service — all data lives in PostgreSQL. No GCS storage is provisioned by default.
- Security:
APP_SECRETauto-generated byUmami Commonand stored in Secret Manager. Inherits Cloud Armor WAF, IAP, Binary Authorization, and VPC Service Controls fromApp CloudRun. - Privacy: No cookies used by default, no personal data stored, fully GDPR/CCPA/PECR compliant.
- Health endpoint: Umami exposes
/api/heartbeatas its health endpoint. Both startup and liveness probes target this path. - CI/CD: Custom Cloud Build image pipeline by default; Cloud Deploy progressive delivery optional.
- Tracking: After deployment, embed
<script async src="https://<your-url>/script.js" data-website-id="..."></script>in tracked websites.
Project & Application Identity
| Variable | Group | Type | Default | Description |
|---|---|---|---|---|
project_id | 1 | string | — | GCP project ID. Required. |
region | 1 | string | 'us-central1' | GCP region for all resources. |
tenant_deployment_id | 2 | string | 'demo' | Short suffix appended to all resource names. |
support_users | 2 | list(string) | [] | Email recipients for monitoring alerts. |
resource_labels | 2 | map(string) | {} | Labels applied to all provisioned resources. |
application_name | 3 | string | 'umami' | Base resource name. Do not change after initial deployment. |
application_display_name | 3 | string | 'Umami' | Human-readable name shown in the GCP Console. |
application_description | 3 | string | 'Umami Analytics on Cloud Run' | Cloud Run service description. |
application_version | 3 | string | 'postgresql-latest' | Umami image version tag. Use a postgresql- prefixed tag (e.g., postgresql-latest, postgresql-v2.11.3). |
Wrapper architecture: Umami CloudRun calls Umami Common to build an application_config object containing Umami-specific environment variables, probe configuration, and the db-init job definition. module_secret_env_vars carries APP_SECRET from Umami Common. The DATABASE_URL is constructed at runtime from the DB_* platform variables injected by App CloudRun. scripts_dir is resolved to abspath("${module.umami_app.path}/scripts") at apply time.
PostgreSQL note: Umami requires PostgreSQL 15. database_type = "POSTGRES_15" is the default and should not be changed.
Default admin credentials: On first deployment, log in with username admin and password umami. Change these immediately after first login.
2. IAM & Access Control
Umami_CloudRun delegates all IAM provisioning to App_CloudRun. The Cloud Run SA, Cloud Build SA, IAP service agent, and password rotation role sets are identical to those in App_CloudRun.
Auto-generated application secret: Umami Common generates APP_SECRET at apply time using random_password (32 characters, no special characters). It is written to Secret Manager and injected into the Cloud Run container as the APP_SECRET environment variable. Plaintext is never stored in Terraform state after initial apply.
Database initialisation identity: The db-init Cloud Run Job runs under the Cloud Run SA. It connects to Cloud SQL PostgreSQL via the Auth Proxy Unix socket (since enable_cloudsql_volume = true by default), using DB_HOST (the socket path under /cloudsql), DB_USER, and DB_PASSWORD (from Secret Manager).
30-second secret propagation delay: Umami Common inserts a propagation wait after creating secrets. The secret_propagation_delay variable (default 30 seconds) controls how long dependent resources wait after secret creation before proceeding.
For the complete role tables and IAP, password rotation, and public access details, see App_CloudRun.
3. Core Service Configuration
A. Compute (Cloud Run)
Umami is a lightweight Next.js application with modest resource requirements. Umami CloudRun exposes cpu_limit and memory_limit as dedicated top-level variables.
Scale-to-zero is supported (min_instance_count = 0 by default). Umami cold starts are fast — the application is lightweight and has no slow startup operations.
Startup CPU Boost is always enabled (hardcoded in App CloudRun).
Container image: container_image_source defaults to 'custom', meaning Cloud Build builds a wrapper image that maps the platform's DB_* environment variables to Umami's DATABASE_URL. Set container_image_source = 'prebuilt' and configure DATABASE_URL manually to deploy the official ghcr.io/umami-software/umami image directly.
| Variable | Group | Default | Description |
|---|---|---|---|
deploy_application | 4 | true | Set false for infrastructure-only deployment (SQL, secrets). |
container_image_source | 4 | 'custom' | 'custom' builds via Cloud Build (maps DB_* to DATABASE_URL). 'prebuilt' deploys an existing image URI. |
container_image | 4 | 'ghcr.io/umami-software/umami' | Base image. Override to use a custom-built image or mirrored copy. |
cpu_limit | 4 | '1000m' | CPU per instance. 1 vCPU is sufficient for most Umami deployments. |
memory_limit | 4 | '512Mi' | Memory per instance. Increase to 1Gi for high-traffic sites. |
min_instance_count | 4 | 0 | Minimum instances. Set to 1 to avoid cold starts. |
max_instance_count | 4 | 3 | Maximum instances. Umami scales horizontally — all state is in PostgreSQL. |
container_port | 4 | 3000 | Umami's native HTTP port. |
execution_environment | 4 | 'gen2' | Gen2 recommended for performance. |
timeout_seconds | 4 | 300 | Max request duration. |
enable_cloudsql_volume | 4 | true | Default true — Umami connects via Unix socket. |
cpu_always_allocated | 4 | false | Request-based billing — CPU is billed only while serving a request. Set true to allocate CPU at all times (instance-based billing). |
traffic_split | 4 | [] | Percentage-based canary/blue-green traffic allocation. |
service_annotations | 4 | {} | Advanced Cloud Run annotations. |
service_labels | 4 | {} | Labels applied to the Cloud Run service. |
Differences from App CloudRun defaults:
| Variable | App CloudRun | Umami CloudRun | Reason |
|---|---|---|---|
container_port | 8080 | 3000 | Umami's native Next.js port. |
cpu_limit | '1000m' | '1000m' | Same — Umami is lightweight. |
memory_limit | '512Mi' | '512Mi' | Same — Umami has modest memory needs. |
max_instance_count | 1 | 3 | Umami scales horizontally safely — all state is in PostgreSQL. |
B. Database (Cloud SQL — PostgreSQL 15)
Umami requires PostgreSQL 15. The module provisions a Cloud SQL PostgreSQL 15 instance and creates a dedicated database and user for Umami.
DATABASE_URL construction: The custom entrypoint (in custom image mode) constructs DATABASE_URL from the platform-injected DB_HOST, DB_USER, DB_PASSWORD, DB_NAME, and DB_PORT variables. This avoids the need to pass a plaintext connection string.
| Variable | Group | Default | Description |
|---|---|---|---|
database_type | 12 | 'POSTGRES_15' | Cloud SQL engine. Must be POSTGRES_15 for Umami. |
application_database_name | 12 | 'umami' | PostgreSQL database name. Do not change after initial deployment. |
application_database_user | 12 | 'umami' | PostgreSQL application user. Password auto-generated and stored in Secret Manager. |
database_password_length | 12 | 32 | Auto-generated password length. Range: 16–64. |
enable_auto_password_rotation | 12 | false | Automated zero-downtime password rotation. |
rotation_propagation_delay_sec | 12 | 90 | Seconds to wait after rotation before restarting the service. |
enable_postgres_extensions | 12 | false | Enables installation of PostgreSQL extensions after provisioning. |
postgres_extensions | 12 | [] | PostgreSQL extensions to install (e.g., ['uuid-ossp']). |
C. Storage (GCS)
No storage by default. Umami is a stateless analytics service — all data lives in PostgreSQL. storage_buckets defaults to an empty list. Provision additional buckets only if needed for custom use cases (e.g., backup exports).
| Variable | Group | Default | Description |
|---|---|---|---|
create_cloud_storage | 11 | true | Controls whether the module provisions GCS buckets defined in storage_buckets. |
storage_buckets | 11 | [] | GCS buckets to provision. Empty by default — Umami requires no file storage. |
enable_nfs | 11 | false | Provisions a Cloud Filestore NFS instance. Not required for Umami. |
nfs_mount_path | 11 | '/mnt/nfs' | Container path where NFS is mounted. Only used when enable_nfs = true. |
gcs_volumes | 11 | [] | GCS buckets to mount via GCS Fuse (requires gen2). |
manage_storage_kms_iam | 11 | false | Creates a CMEK KMS keyring and enables CMEK on all storage buckets. |
enable_artifact_registry_cmek | 11 | false | Creates an Artifact Registry KMS key for at-rest image encryption. |
D. Networking
Cloud Run uses Direct VPC Egress to reach Cloud SQL's internal IP. Because enable_cloudsql_volume = true is the default, the Auth Proxy sidecar handles the Cloud SQL connection via Unix socket.
| Variable | Group | Default | Description |
|---|---|---|---|
ingress_settings | 5 | 'all' | 'all' — public internet; 'internal' — VPC only; 'internal-and-cloud-load-balancing' — forces traffic through the HTTPS Load Balancer. |
vpc_egress_setting | 5 | 'PRIVATE_RANGES_ONLY' | 'PRIVATE_RANGES_ONLY' routes only RFC 1918 traffic via VPC. 'ALL_TRAFFIC' routes all egress via VPC. |
E. Initialization & Bootstrap
A db-init Cloud Run Job is automatically provisioned by Umami Common when initialization_jobs is left as the default empty list ([]). It uses a PostgreSQL client image and executes Umami_Common/scripts/db-init.sh, which performs the following idempotent operations:
- Connects to Cloud SQL PostgreSQL via the Auth Proxy Unix socket.
- Creates the
umamidatabase user with the password from Secret Manager. - Creates the
umamidatabase if it does not exist. - Grants the
umamiuser full privileges on the database.
Umami itself runs its own Prisma-based database migrations on first startup — the db-init job only pre-creates the database and user so that Umami's migrations can run successfully.
| Variable | Group | Default | Description |
|---|---|---|---|
initialization_jobs | 13 | [] | One-shot Cloud Run Jobs. Leave empty for Umami Common to supply the default db-init job. Non-empty list replaces it entirely. |
cron_jobs | 13 | [] | Recurring jobs triggered by Cloud Scheduler. |
additional_services | 13 | [] | Additional Cloud Run services deployed alongside the main application. |
4. Advanced Security
A. Cloud Armor WAF
When enable_cloud_armor = true, a Global HTTPS Load Balancer with a Cloud Armor WAF policy (OWASP Top 10, adaptive DDoS, 500 req/min rate limiting) is provisioned in front of Cloud Run.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_cloud_armor | 10 | false | Provisions Global HTTPS LB + Cloud Armor WAF. Required for custom domains, CDN, and DDoS protection. |
admin_ip_ranges | 10 | [] | CIDR ranges exempted from WAF rules (e.g., office VPN, CI/CD egress IPs). |
B. Identity-Aware Proxy (IAP)
When enable_iap = true, Cloud Run's native IAP integration is enabled directly on the service. Google identity authentication is required before requests reach Umami. Useful for restricting analytics access to internal team members only.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_iap | 5 | false | Enables IAP natively on the Cloud Run service. |
iap_authorized_users | 5 | [] | Users/service accounts granted access. Format: 'user:email' or 'serviceAccount:sa@...'. |
iap_authorized_groups | 5 | [] | Google Groups granted access. Format: 'group:name@example.com'. |
Note: IAP protects the Umami dashboard. The tracking script (/script.js) and event endpoint (/api/send) must remain publicly accessible for tracked websites to report data. If applying IAP, ensure tracking paths are exempted or use a separate ingress configuration.
C. Binary Authorization
When enable_binary_authorization = true, Cloud Run enforces that deployed images carry a valid cryptographic attestation.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_binary_authorization | 8 | false | Enforces image attestation. Requires a Binary Authorization policy and attestor pre-configured in the project. |
D. VPC Service Controls
When enable_vpc_sc = true, all GCP API calls from this module are bound within an existing VPC-SC perimeter.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_vpc_sc | 22 | false | Registers module API calls within the project's VPC-SC perimeter. A perimeter must already exist before enabling. |
E. Secret Manager Integration
Umami's APP_SECRET is stored in Secret Manager and injected natively by Cloud Run at revision start — plaintext is never written to Terraform state.
| Variable | Group | Default | Description |
|---|---|---|---|
secret_environment_variables | 6 | {} | Map of env var name → Secret Manager secret ID. Resolved at runtime; never stored in state. |
secret_rotation_period | 6 | '2592000s' | Frequency at which Secret Manager emits rotation notifications. Default: 30 days. |
secret_propagation_delay | 6 | 30 | Seconds to wait after secret creation before dependent resources proceed. |
5. Traffic & Ingress
A. HTTPS Load Balancer
When enable_cloud_armor = true, a Global HTTPS Load Balancer backed by a Serverless NEG is provisioned. Traffic flows: Internet → Cloud Armor → Global HTTPS LB → Serverless NEG → Cloud Run.
Setting ingress_settings = 'internal-and-cloud-load-balancing' forces all Umami traffic through the LB, preventing direct *.run.app URL access.
See App_CloudRun for full architecture details.
B. Cloud CDN
When enable_cdn = true (requires enable_cloud_armor = true), Cloud CDN is attached to the HTTPS Load Balancer backend.
Umami consideration: Umami's dashboard is a dynamic Next.js application. Cloud CDN is useful for caching Umami's static assets (JavaScript bundles, CSS) but should not cache API responses (/api/*) which contain live analytics data. Ensure appropriate Cache-Control headers are set.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_cdn | 10 | false | Enables Cloud CDN on the HTTPS LB backend. Only effective when enable_cloud_armor = true. |
max_images_to_retain | 10 | 7 | Maximum number of recent container images to keep in Artifact Registry. Set 0 to disable. |
delete_untagged_images | 10 | true | Automatically deletes untagged (dangling) images from Artifact Registry. |
image_retention_days | 10 | 30 | Days after which images are eligible for deletion. Set 0 to disable age-based deletion. |
C. Custom Domains
Custom domains are attached to the Global HTTPS Load Balancer via application_domains. Google-managed SSL certificates are provisioned automatically. DNS must point to the load balancer IP after apply.
| Variable | Group | Default | Description |
|---|---|---|---|
application_domains | 10 | [] | Custom domain names for the HTTPS LB. Google-managed SSL certificates provisioned per domain. (e.g., ['analytics.example.com']) |
After the first apply, retrieve the LB IP and create an A record. SSL certificate provisioning takes 10–30 minutes after DNS propagation.
6. CI/CD & Delivery
A. Cloud Build Triggers
When enable_cicd_trigger = true, a Cloud Build GitHub connection and push trigger are provisioned. The trigger builds and deploys a custom Umami image when code is pushed to the configured branch.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_cicd_trigger | 8 | false | Provisions a Cloud Build GitHub trigger. Requires github_repository_url and credentials. |
github_repository_url | 8 | "" | Full HTTPS URL of the GitHub repository. |
github_token | 8 | "" | GitHub PAT (repo, admin:repo_hook scopes). Required on first apply. Sensitive. |
github_app_installation_id | 8 | "" | GitHub App installation ID (preferred for organisation repos). |
cicd_trigger_config | 8 | { branch_pattern = "^main$" } | Advanced trigger config: branch_pattern, included_files, ignored_files, trigger_name, substitutions. |
B. Cloud Deploy Pipeline
When enable_cloud_deploy = true (requires enable_cicd_trigger = true), the CI/CD pipeline is upgraded to a managed Cloud Deploy delivery pipeline with sequential promotion stages.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_cloud_deploy | 8 | false | Provisions a Cloud Deploy pipeline. Requires enable_cicd_trigger = true. |
cloud_deploy_stages | 8 | [dev, staging, prod(approval)] | Ordered promotion stages. Each: name, target_name, service_name, require_approval, auto_promote. |
7. Reliability & Scheduling
A. Scaling & Concurrency
min_instance_count = 0 (scale-to-zero) by default. Umami cold starts are fast — the Next.js application starts in a few seconds with no heavy first-boot operations. max_instance_count = 3 by default. Umami scales horizontally safely because all state is in PostgreSQL.
B. Traffic Splitting
Traffic splitting is supported. Umami stores all analytics data in PostgreSQL (shared across all instances), making canary deployments safe — any revision can serve requests and write to the same database.
| Variable | Group | Default | Description |
|---|---|---|---|
traffic_split | 4 | [] | Percentage-based traffic allocation across named revisions. All entries must sum to 100. Empty sends 100% to the latest revision. |
C. Health Probes & Uptime Monitoring
Umami exposes /api/heartbeat as its dedicated health endpoint. Both the startup and liveness probes target this path.
| Variable | Group | Default | Description |
|---|---|---|---|
startup_probe | 14 | { path="/api/heartbeat", initial_delay_seconds=30, timeout_seconds=10, period_seconds=10, failure_threshold=30 } | Startup readiness probe. Container receives no traffic until this succeeds. |
liveness_probe | 14 | { path="/api/heartbeat", initial_delay_seconds=30, timeout_seconds=10, period_seconds=30, failure_threshold=3 } | Liveness probe. Container is restarted after failure_threshold consecutive failures. |
startup_probe_config | 14 | { enabled=true, path="/api/heartbeat", initial_delay_seconds=30 } | Cloud Run startup probe (passed directly to App CloudRun). |
health_check_config | 14 | { enabled=true, path="/api/heartbeat" } | Cloud Run liveness probe (passed directly to App CloudRun). |
uptime_check_config | 14 | { enabled=false, path="/api/heartbeat" } | Cloud Monitoring uptime check (disabled by default). When enabled, alerts notify support_users if unreachable. |
alert_policies | 14 | [] | Cloud Monitoring metric alert policies. |
D. Auto Password Rotation
When enable_auto_password_rotation = true, a zero-downtime password rotation pipeline is provisioned:
- Secret Manager emits a rotation notification at every
secret_rotation_periodinterval. - Eventarc fires a Cloud Run rotation Job.
- The job generates a new password, updates the Cloud SQL PostgreSQL user, writes a new secret version.
- After
rotation_propagation_delay_secseconds, the job restarts the Umami service.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_auto_password_rotation | 12 | false | Enables automated password rotation. |
rotation_propagation_delay_sec | 12 | 90 | Seconds to wait after writing the new secret before restarting the service. |
secret_rotation_period | 6 | '2592000s' | Rotation frequency. Default: 30 days. |
8. Integrations
A. Redis Cache
Redis is disabled by default (enable_redis = false). Umami does not require Redis — all state is stored in PostgreSQL. Enable only if integrating an external caching layer for custom use cases.
| Variable | Group | Default | Description |
|---|---|---|---|
enable_redis | 16 | false | Enables Redis by injecting REDIS_HOST and REDIS_PORT env vars. Umami does not require Redis. |
redis_host | 16 | "" | Redis server hostname or IP. Only used when enable_redis = true. |
redis_port | 16 | '6379' | Redis server TCP port (string). |
B. Backup Import & Recovery
When enable_backup_import = true, a dedicated Cloud Run Job restores an existing database backup into the provisioned Cloud SQL PostgreSQL instance.
| Variable | Group | Default | Description |
|---|---|---|---|
backup_schedule | 7 | '0 2 * * *' | Cron expression (UTC) for automated daily backups. |
backup_retention_days | 7 | 7 | Days to retain backup files in GCS. |
enable_backup_import | 7 | false | Triggers a one-time restore on apply. Set false after a successful import. |
backup_source | 7 | 'gcs' | 'gcs' (full GCS URI) or 'gdrive' (Drive file ID). |
backup_file | 7 | 'backup.sql' | Filename of the backup to import. |
backup_format | 7 | 'sql' | Backup file format. Options: sql, tar, gz, tgz, tar.gz, zip, auto. |
C. Observability & Alerting
| Variable | Group | Default | Description |
|---|---|---|---|
uptime_check_config | 14 | { enabled=false, path="/api/heartbeat" } | Uptime check configuration: enabled, path, check_interval, timeout. Disabled by default. |
alert_policies | 14 | [] | Metric alert policies. Each: name, metric_type, comparison, threshold_value, duration_seconds, aggregation_period. |
support_users | 2 | [] | Email addresses notified by uptime and alert policy triggers. |
D. Exploring with the GCP Console
After a successful deployment, explore the Umami installation in the GCP Console:
Cloud Run service:
Navigate to Cloud Run in the GCP Console. Find the service named app<application_name><tenant_deployment_id><deployment_id> (e.g., appumamidemoxyz). Click the service to view:
- The service URL — use this to access the Umami dashboard.
- Revisions tab — lists all deployed revisions with traffic percentages.
- Logs tab — streams container logs including Umami startup and request logs.
- Metrics tab — shows request count, latency, instance count, and CPU/memory utilisation.
- Configuration tab — shows the current container image, environment variables (non-sensitive), and resource limits.
Cloud SQL instance:
Navigate to SQL in the GCP Console. Find the instance named app-sql-<deployment_id>. Click the instance to view:
- Overview — connection name, region, PostgreSQL version, storage usage.
- Databases — the
umamidatabase. - Users — the
umamiapplication user. - Connections — active connections from the Cloud Run service via Auth Proxy.
- Backups — automated backup schedule and restore points.
Secret Manager:
Navigate to Security → Secret Manager. Find secrets named secret-<tenant_resource_prefix>-<application_name>-app-secret (or similar). Click a secret to view:
- Secret versions and creation timestamps.
- Access log entries showing when Cloud Run read the secret.
- Replication policy.
Artifact Registry: Navigate to Artifact Registry. Find the repository for this deployment. Click to view mirrored Umami images, their tags, sizes, and retention policy status.
Cloud Monitoring uptime checks:
If uptime_check_config.enabled = true was set, navigate to Monitoring → Uptime checks and find the check for this Umami deployment targeting /api/heartbeat. View check results, response time, and failure history across GCP regions.
E. Exploring with gcloud
Use these gcloud commands to inspect the Umami deployment from the command line. Replace PROJECT_ID, REGION, and DEPLOYMENT_ID with your values.
# List Cloud Run services in the project
gcloud run services list \
--project=PROJECT_ID \
--region=REGION \
--format="table(name,status.url,status.conditions[0].type)"
# Describe the Umami Cloud Run service
gcloud run services describe SERVICE_NAME \
--project=PROJECT_ID \
--region=REGION \
--format="yaml(status.url,spec.template.spec.containers[0].resources)"
# View the latest revision
gcloud run revisions list \
--project=PROJECT_ID \
--region=REGION \
--service=SERVICE_NAME \
--format="table(name,status.conditions[0].type,spec.containerConcurrency,metadata.creationTimestamp)"
# Tail Cloud Run logs
gcloud run services logs tail SERVICE_NAME \
--project=PROJECT_ID \
--region=REGION
# Check Cloud SQL instance status
gcloud sql instances describe INSTANCE_NAME \
--project=PROJECT_ID \
--format="table(name,state,databaseVersion,settings.tier)"
# List databases in the Cloud SQL instance
gcloud sql databases list \
--instance=INSTANCE_NAME \
--project=PROJECT_ID
# List Cloud SQL users
gcloud sql users list \
--instance=INSTANCE_NAME \
--project=PROJECT_ID
# List Secret Manager secrets for this deployment
gcloud secrets list \
--project=PROJECT_ID \
--filter="name~umami" \
--format="table(name,createTime,replication.auto)"
# Describe the APP_SECRET secret
gcloud secrets describe SECRET_NAME \
--project=PROJECT_ID
# View uptime check results (Cloud Monitoring)
gcloud monitoring uptime list-configs \
--project=PROJECT_ID \
--format="table(displayName,httpCheck.path,period,timeout)"
# List Artifact Registry images for Umami
gcloud artifacts docker images list \
REGION-docker.pkg.dev/PROJECT_ID/REPOSITORY_NAME \
--project=PROJECT_ID \
--format="table(image,tags,createTime)"
# Check Cloud Run IAM policy
gcloud run services get-iam-policy SERVICE_NAME \
--project=PROJECT_ID \
--region=REGION
# View Cloud Build triggers for this deployment
gcloud builds triggers list \
--project=PROJECT_ID \
--filter="name~umami" \
--format="table(name,createTime,github.push.branch)"
9. Platform-Managed Behaviours
The following behaviours are applied automatically by Umami CloudRun regardless of variable values. They cannot be overridden via tfvars.
| Behaviour | Implementation | Detail |
|---|---|---|
| PostgreSQL 15 required | database_type = "POSTGRES_15" default | Umami requires PostgreSQL. MySQL and other engines are unsupported. |
| DATABASE_URL constructed at runtime | Custom entrypoint in Umami Common image | The custom image mode builds a wrapper that assembles DATABASE_URL from platform-injected DB_* variables. Using prebuilt mode requires manual DATABASE_URL configuration. |
| APP_SECRET auto-generated | random_password in Umami Common | 32-character alphanumeric secret, stored in Secret Manager, injected as APP_SECRET. |
| No storage buckets by default | storage_buckets = [] default | Umami is stateless — no GCS buckets are provisioned unless explicitly configured. |
| Image mirroring enabled by default | enable_image_mirroring = true | Mirrors from GitHub Container Registry (ghcr.io) into Artifact Registry to avoid rate limits. |
| Default db-init job | Supplied by Umami Common when initialization_jobs = [] | PostgreSQL database and user created automatically before Umami runs its own migrations. |
| Prisma migrations at startup | Umami container behaviour | Umami runs its own Prisma database migrations on each startup. The db-init job only pre-creates the database and user. |
| No Redis required | enable_redis = false default | Umami uses only PostgreSQL for all data storage. Redis is not required. |
| Scripts directory | scripts_dir = abspath("${module.umami_app.path}/scripts") | Initialization scripts are sourced from Umami Common, not from the deployment directory. |
Inline infrastructure (when no Services_GCP stack is present) is identical to App_CloudRun §9 — App_CloudRun provisions an inline VPC, Cloud NAT, Cloud SQL instance, service accounts, and GCP APIs as required. See App_CloudRun for the full inline resource inventory and teardown notes.
10. Variable Reference
All user-configurable variables exposed by Umami CloudRun, sorted by UI group then order. | Variable | Group | Default | Description |
|---|---|---|---|
| resource_creator_identity | 0 | (platform SA) | Service account used by Terraform to manage resources. |
| project_id | 1 | — | GCP project ID. Required. |
| region | 1 | 'us-central1' | GCP region for all resources. |
| tenant_deployment_id | 2 | 'demo' | Short suffix appended to all resource names. |
| support_users | 2 | [] | Email addresses for monitoring alerts. |
| resource_labels | 2 | {} | Labels applied to all provisioned resources. |
| application_name | 3 | 'umami' | Base resource name. Do not change after initial deployment. |
| application_display_name | 3 | 'Umami' | Human-readable name shown in the GCP Console. |
| application_description | 3 | 'Umami Analytics on Cloud Run' | Service description. |
| application_version | 3 | 'postgresql-latest' | Umami container image tag. Use postgresql- prefixed tags. |
| deploy_application | 4 | true | Set false for infrastructure-only deployment. |
| container_image_source | 4 | 'custom' | 'custom' (Cloud Build) or 'prebuilt' (existing image). |
| container_image | 4 | 'ghcr.io/umami-software/umami' | Container image URI. |
| container_build_config | 4 | { enabled=true } | Cloud Build config for custom image builds. |
| enable_image_mirroring | 4 | true | Mirrors Umami image into Artifact Registry. |
| cpu_limit | 4 | '1000m' | CPU per instance. |
| memory_limit | 4 | '512Mi' | Memory per instance. |
| min_instance_count | 4 | 0 | Minimum instances (0 = scale-to-zero). |
| max_instance_count | 4 | 3 | Maximum instances. |
| container_port | 4 | 3000 | Umami's native port. |
| container_protocol | 4 | 'http1' | 'http1' or 'h2c'. |
| execution_environment | 4 | 'gen2' | Cloud Run execution environment generation. |
| timeout_seconds | 4 | 300 | Max request duration. |
| cpu_always_allocated | 4 | false | Request-based billing by default. Set true to allocate CPU at all times. |
| enable_cloudsql_volume | 4 | true | Mount Cloud SQL Auth Proxy Unix socket. |
| cloudsql_volume_mount_path | 4 | '/cloudsql' | Container path for Auth Proxy socket. |
| traffic_split | 4 | [] | Canary/blue-green traffic allocation. |
| max_revisions_to_retain | 4 | 7 | Maximum Cloud Run revisions to keep. |
| service_annotations | 4 | {} | Advanced Cloud Run annotations. |
| service_labels | 4 | {} | Labels applied to the Cloud Run service. |
| ingress_settings | 5 | 'all' | 'all', 'internal', or 'internal-and-cloud-load-balancing'. |
| vpc_egress_setting | 5 | 'PRIVATE_RANGES_ONLY' | 'PRIVATE_RANGES_ONLY' or 'ALL_TRAFFIC'. |
| enable_iap | 5 | false | Enables IAP natively on the Cloud Run service. |
| iap_authorized_users | 5 | [] | Users/SAs granted IAP access. |
| iap_authorized_groups | 5 | [] | Google Groups granted IAP access. |
| environment_variables | 6 | {} | Plain-text env vars (e.g., { DISABLE_TELEMETRY = "1" }). |
| secret_environment_variables | 6 | {} | Secret Manager references. |
| secret_propagation_delay | 6 | 30 | Seconds to wait after secret creation. |
| secret_rotation_period | 6 | '2592000s' | Secret Manager rotation notification frequency. |
| backup_schedule | 7 | '0 2 * * *' | Cron expression (UTC) for automated backups. |
| backup_retention_days | 7 | 7 | Days to retain backup files in GCS. |
| enable_backup_import | 7 | false | Triggers a one-time restore on apply. |
| backup_source | 7 | 'gcs' | 'gcs' or 'gdrive'. |
| backup_file | 7 | 'backup.sql' | Filename of the backup to import. |
| backup_format | 7 | 'sql' | Backup format: sql, tar, gz, tgz, tar.gz, zip, auto. |
| enable_cicd_trigger | 8 | false | Provisions a Cloud Build GitHub trigger. |
| github_repository_url | 8 | "" | Full HTTPS URL of the GitHub repository. |
| github_token | 8 | "" | GitHub PAT. Sensitive. |
| github_app_installation_id | 8 | "" | GitHub App installation ID. |
| cicd_trigger_config | 8 | { branch_pattern = "^main$" } | Advanced Cloud Build trigger config. |
| enable_cloud_deploy | 8 | false | Provisions a Cloud Deploy progressive delivery pipeline. |
| cloud_deploy_stages | 8 | [dev, staging, prod(approval)] | Ordered Cloud Deploy promotion stages. |
| enable_binary_authorization | 8 | false | Enforces image attestation on deployment. |
| binauthz_evaluation_mode | 8 | 'ALWAYS_ALLOW' | Binary Authorization enforcement mode. Not referenced in this module. |
| enable_custom_sql_scripts | 9 | false | Runs SQL scripts from GCS after provisioning. |
| custom_sql_scripts_bucket | 9 | "" | GCS bucket containing SQL scripts. |
| custom_sql_scripts_path | 9 | "" | Path prefix within the bucket. |
| custom_sql_scripts_use_root | 9 | false | Run scripts as the root DB user. |
| enable_cloud_armor | 10 | false | Provisions Global HTTPS LB + Cloud Armor WAF. |
| admin_ip_ranges | 10 | [] | CIDR ranges exempted from WAF rules. |
| application_domains | 10 | [] | Custom domains with Google-managed SSL certificates. |
| enable_cdn | 10 | false | Enables Cloud CDN on the HTTPS LB backend. |
| max_images_to_retain | 10 | 7 | Maximum number of container images to keep in Artifact Registry. |
| delete_untagged_images | 10 | true | Automatically deletes untagged images from Artifact Registry. |
| image_retention_days | 10 | 30 | Days after which images are eligible for deletion. |
| create_cloud_storage | 11 | true | Controls GCS bucket provisioning. |
| storage_buckets | 11 | [] | GCS buckets to provision. Empty by default — Umami needs no file storage. |
| enable_nfs | 11 | false | Provisions NFS shared storage. Not required for Umami. |
| nfs_mount_path | 11 | '/mnt/nfs' | Container NFS mount path. |
| gcs_volumes | 11 | [] | GCS buckets to mount via GCS Fuse. |
| manage_storage_kms_iam | 11 | false | Creates CMEK KMS key for storage buckets. |
| enable_artifact_registry_cmek | 11 | false | Creates Artifact Registry KMS key for at-rest image encryption. |
| database_type | 12 | 'POSTGRES_15' | Cloud SQL engine. Must be POSTGRES_15 for Umami. |
| application_database_name | 12 | 'umami' | PostgreSQL database name. Do not change after initial deployment. |
| application_database_user | 12 | 'umami' | PostgreSQL application user. |
| database_password_length | 12 | 32 | Auto-generated password length. Range: 16–64. |
| enable_auto_password_rotation | 12 | false | Automated zero-downtime password rotation. |
| rotation_propagation_delay_sec | 12 | 90 | Seconds to wait after rotation before restarting the service. |
| enable_postgres_extensions | 12 | false | Enables installation of PostgreSQL extensions. |
| postgres_extensions | 12 | [] | PostgreSQL extensions to install. |
| initialization_jobs | 13 | [] | One-shot Cloud Run Jobs. Leave empty for Umami Common to supply the default db-init job. |
| cron_jobs | 13 | [] | Recurring scheduled Cloud Run Jobs. |
| additional_services | 13 | [] | Additional Cloud Run services deployed alongside Umami. |
| startup_probe | 14 | { path="/api/heartbeat", initial_delay_seconds=30, failure_threshold=30 } | Startup probe. |
| liveness_probe | 14 | { path="/api/heartbeat", initial_delay_seconds=30, failure_threshold=3 } | Liveness probe. |
| startup_probe_config | 14 | { enabled=true, path="/api/heartbeat" } | Cloud Run startup probe (passed directly to App CloudRun). |
| health_check_config | 14 | { enabled=true, path="/api/heartbeat" } | Cloud Run liveness probe (passed directly to App CloudRun). |
| uptime_check_config | 14 | { enabled=false, path="/api/heartbeat" } | Cloud Monitoring uptime check (disabled by default). |
| alert_policies | 14 | [] | Cloud Monitoring metric alert policies. |
| enable_redis | 16 | false | Enables Redis env var injection. Not required for Umami. |
| redis_host | 16 | "" | Redis hostname/IP. Only used when enable_redis = true. |
| redis_port | 16 | '6379' | Redis TCP port (string). |
| enable_vpc_sc | 22 | false | Registers API calls within the project's VPC-SC perimeter. |
| vpc_cidr_ranges | 22 | [] | VPC subnet CIDR ranges for VPC-SC network access level. |
| vpc_sc_dry_run | 22 | true | Logs VPC-SC violations without blocking. |
| organization_id | 22 | "" | GCP Organization ID for VPC-SC. Auto-discovered from project when empty. |
| enable_audit_logging | 22 | false | Enables detailed Cloud Audit Logs. |
11. Outputs
| Output | Description |
|---|---|
service_name | Name of the Cloud Run service. |
service_url | Public URL of the Cloud Run service. |
service_location | GCP region where the Cloud Run service is deployed. |
project_id | GCP project ID. |
deployment_id | Deployment ID suffix used in resource names. |
database_instance_name | Name of the Cloud SQL PostgreSQL instance. |
database_name | Name of the application database. |
database_user | Name of the application database user. |
database_password_secret | Secret Manager secret name for the database password. |
storage_buckets | Created GCS storage buckets. |
container_image | Container image used for the deployment. |
cicd_enabled | Whether the CI/CD pipeline is enabled. |
github_repository_url | GitHub repository URL connected for CI/CD. |
Configuration Pitfalls & Sensible Defaults
Risk levels: Critical (data loss, full outage, security breach) — High (service unavailable or significant degradation) — Medium (degraded function or increased cost) — Low (minor impact).
| Variable | Sensible Default | Risk | Consequence of Incorrect Value |
|---|---|---|---|
project_id | (required) | Critical | No default — deployment fails immediately. |
database_type | "POSTGRES_15" | Critical | Umami only supports PostgreSQL. Setting to MySQL or another engine will break the DATABASE_URL construction and cause Umami to fail at startup with a database connection error. |
application_database_name | "umami" | Critical | Immutable after first deployment — changing this causes Terraform to recreate the database, destroying all analytics data collected by Umami. |
application_database_user | "umami" | Critical | Immutable after first deployment — changing this recreates the Cloud SQL user, invalidating all stored credentials and breaking Umami's database connection. |
container_port | 3000 | Critical | Umami listens on port 3000. Changing this without matching the container's bound port causes Cloud Run health probes to fail and the service to be marked unhealthy. |
container_image_source | "custom" | High | The custom mode builds a wrapper image that assembles DATABASE_URL from DB_* variables. Using "prebuilt" with the official Umami image requires manually providing DATABASE_URL as an environment variable — without it, Umami cannot connect to PostgreSQL and will crash on startup. |
application_version | "postgresql-latest" | Medium | Always use a postgresql- prefixed tag (e.g., postgresql-latest, postgresql-v2.11.3). Plain version tags (e.g., latest) are not published for the PostgreSQL variant of Umami and will result in a container pull error. |
admin_password | (change on first login) | Critical | The default Umami admin credentials are admin / umami — widely known. Failure to change these on first login exposes your analytics dashboard and all tracked data to anyone who knows the URL. |
enable_iap | false | Medium | Without IAP, anyone who knows the Cloud Run URL can access the Umami dashboard login page. Umami's own authentication is the only barrier. Enable IAP or ensure ingress_settings = "internal" for private deployments. |
ingress_settings | "all" | Medium | "all" exposes Umami publicly. The tracking script endpoint must be publicly accessible for tracked websites to report data, but the admin dashboard should be restricted for sensitive deployments. Consider using IAP or a custom domain with restricted access for the admin paths. |
min_instance_count | 0 | Low | Scale-to-zero is safe for Umami — cold starts are fast (a few seconds). However, the first request after a cold start will experience slight latency. Set to 1 for time-sensitive analytics dashboards. |
memory_limit | "512Mi" | Medium | 512Mi is the default and sufficient for moderate traffic. High-traffic sites with many concurrent dashboard users or complex queries may experience OOM. Increase to 1Gi if you observe memory pressure in Cloud Monitoring. |
enable_backup_import | false | Critical | Requires a valid backup file to be accessible at backup_file. Enabling with an invalid path causes the restore Cloud Run job to fail during apply. |
backup_retention_days | 7 | Medium | Seven days is minimal for production analytics. Losing analytics history has direct business impact. Increase to 30+ days for any long-term analytics deployment. |
secret_propagation_delay | 30 | Low | Occasionally insufficient in multi-region setups. Increase to 60–90s if secret-read errors are observed during apply. |
vpc_sc_dry_run | true | Medium | VPC-SC dry run logs violations but does not block them. After verifying no false positives in Cloud Logging, switch to false for actual enforcement. Leaving as true permanently provides no security benefit. |
Destroying Resources
Known Deletion Issue: Serverless IPv4 Address Release
When destroying a Cloud Run deployment, you may encounter an error similar to:
Error: Error waiting for Subnetwork to be deleted: The following serverless IPv4 address(es) on subnet ... are still in use.
Cause: GCP holds serverless IPv4 addresses on the VPC subnet asynchronously after a Cloud Run service is deleted. These addresses are released by GCP approximately 20–30 minutes after the Cloud Run service is removed. Terraform/OpenTofu cannot complete the subnet or VPC deletion until they are fully released.
Resolution: Wait 20–30 minutes after the initial destroy attempt, then re-run the destroy command:
tofu destroy
The second run will succeed once GCP has released the reserved addresses.