Skip to main content

Certification track: Associate Cloud Engineer (ACE)

Cal.diy on Google Cloud Run

Cal.diy on Google Cloud Run

Cal.diy is the MIT-licensed, self-hostable fork of Cal.com — the open-source scheduling platform used by millions worldwide to eliminate back-and-forth meeting coordination. This module deploys Cal.diy 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 Cal.diy 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.


1. Overview

Cal.diy runs as a Next.js (Node.js) container on Cloud Run v2. The deployment wires together a focused set of Google Cloud services:

CapabilityGoogle Cloud serviceNotes
ComputeCloud Run v2Next.js service, 2 vCPU / 2 GiB by default, request-based autoscaling
DatabaseCloud SQL for PostgreSQL 15Required — Cal.diy uses Prisma ORM targeting PostgreSQL
Object storageCloud StorageA data bucket provisioned by default
SecretsSecret ManagerAuto-generated NEXTAUTH_SECRET and CALENDSO_ENCRYPTION_KEY
IngressCloud Run URL / Cloud Load BalancingDefault run.app URL, optional external HTTPS load balancer + custom domain

Sensible defaults worth knowing up front:

  • PostgreSQL 15 is mandatory. Selecting MySQL or NONE breaks startup.
  • Scale-to-zero is the default (min_instance_count = 0). Cal.diy's first-boot startup takes 4–5 minutes; set min_instance_count = 1 for production to avoid cold-start latency.
  • A custom wrapper image is built by default. CalDiy_Common always sets image_source = "custom" and provides a Dockerfile. The entrypoint assembles DATABASE_URL from the DB_* environment variables injected by the platform, making the connection robust regardless of which image version is deployed.
  • Three initialization jobs run on first deploy: db-init (PostgreSQL setup), db-migrate (Prisma schema migrations), and seed-app-store (seeds the Cal.diy app store table). All are idempotent.
  • NEXTAUTH_SECRET and CALENDSO_ENCRYPTION_KEY are generated automatically and stored in Secret Manager; you never set them in plain text.
  • NEXT_PUBLIC_WEBAPP_URL and NEXTAUTH_URL are auto-computed from the predicted Cloud Run service URL. Override via environment_variables when using a custom domain.
  • calcom/cal.diy has no latest tag — always pin application_version to a versioned release (e.g., v6.2.0).
  • Redis is disabled by default. NextAuth.js sessions are stored in PostgreSQL. Enable Redis for high-concurrency multi-instance deployments.

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 Cal.diy service

Cal.diy 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. Cloud SQL for PostgreSQL 15

Cal.diy stores all application data (bookings, users, schedules, integrations) in a managed Cloud SQL for PostgreSQL 15 instance. The service connects privately through the Cloud SQL Auth Proxy over a Unix socket (no public IP). On first deploy a sequence of Cloud Run Jobs creates the database and user, runs Prisma schema migrations, and seeds the app store.

  • Console: SQL → select the instance for connections, backups, flags, metrics.
  • CLI:
    gcloud sql instances list --project "$PROJECT"
    gcloud sql instances describe <instance-name> --project "$PROJECT"
    gcloud sql connect <instance-name> --user=<db-user> --database=<db-name> --project "$PROJECT"

The instance name, database, user, and password secret are in the Outputs. See App_CloudRun for the connection model, backups, and password rotation.

C. Cloud Storage

A default Cloud Storage bucket (suffix data) is provisioned and the service account is granted access automatically. Cal.diy does not require shared NFS storage by default — the database stores all booking state.

  • Console: Cloud Storage → Buckets.
  • CLI:
    gcloud storage buckets list --project "$PROJECT"
    gcloud storage ls gs://<data-bucket>/ # bucket name is in the Outputs

See App_CloudRun for GCS Fuse, NFS, and CMEK options.

D. Secret Manager

NEXTAUTH_SECRET (NextAuth.js session signing) and CALENDSO_ENCRYPTION_KEY (Cal.diy data encryption) are generated automatically and stored as Secret Manager secrets. The database password is also managed here. Secrets are injected into the service at runtime; plaintext never appears in configuration.

  • Console: Security → Secret Manager.
  • CLI:
    gcloud secrets list --project "$PROJECT"
    gcloud secrets versions access latest --secret=<secret-name> --project "$PROJECT"

See App_CloudRun for injection and rotation details.

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

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

F. Cloud Logging & Monitoring

Container logs flow to Cloud Logging; Cloud Run and Cloud SQL metrics flow to Cloud Monitoring, with optional uptime checks and alert policies.

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

3. Cal.diy Application Behaviour

  • First-deploy initialization sequence. Three Cloud Run Jobs execute in order before the service serves traffic:

    JobImagePurpose
    db-initpostgres:15-alpineCreates the PostgreSQL database and user, grants privileges
    db-migrateCal.diy app imageRuns prisma migrate deploy to apply the full schema
    seed-app-storeCal.diy app imageSeeds the App table with available integrations

    All three are idempotent and safe to re-run. Inspect their executions:

    gcloud run jobs list --project "$PROJECT" --region "$REGION"
    gcloud run jobs executions list --job <job-name> --project "$PROJECT" --region "$REGION"
  • DATABASE_URL assembly. The entrypoint script assembles DATABASE_URL and DATABASE_DIRECT_URL from DB_* environment variables at container start, then launches the Next.js server. This makes database connectivity independent of which image variant is deployed.

  • Startup probe. Health probes target /api/auth/session (HTTP 200 when NextAuth is ready). CalDiy_Common sets a 6-minute total startup window (initial_delay=180s, failure_threshold=18, period=10s) to accommodate replace-placeholder.sh (~2.5 min), db-migrate (~60s), and seed-app-store (~30s) that run on the first boot inside the container's start.sh.

  • Public URL wiring. NEXT_PUBLIC_WEBAPP_URL and NEXTAUTH_URL are auto-computed from the predicted Cloud Run service URL. Override both in environment_variables when using a custom domain so OAuth callbacks and booking links point to the correct host.

  • Email (SMTP). Cal.diy uses SMTP for booking confirmations, cancellation notices, reminders, and password resets. Configure SMTP_HOST, SMTP_PORT, SMTP_USER, EMAIL_FROM in environment_variables and store SMTP_PASSWORD as a secret_environment_variables reference before going live.

  • No scheduled tasks required. Cal.diy does not require separately scheduled background jobs — bookings and reminders are handled by Next.js API routes.


4. Configuration Variables

Variables are grouped exactly as they appear on the deployment platform. Only settings specific to or notable for Cal.diy 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_namecaldiyBase name for resources. Do not change after first deploy.
display_nameCal.com SchedulingFriendly name shown in the Console.
description(set)Service description.
application_versionv6.2.0Cal.diy image version tag — no latest tag exists, always pin to a versioned release.

Group 4 — Runtime & Scaling

VariableDefaultDescription
deploy_applicationtrueSet false to provision infrastructure only.
container_image_sourcecustomcustom builds the wrapper image via Cloud Build (required for Cloud Run — assembles DATABASE_URL); prebuilt deploys the official image directly.
container_image""Override container image URI. Leave empty to use default.
cpu_limit2000mCPU per instance.
memory_limit2GiMemory per instance; raise to 4Gi for production multi-user load.
container_port3000Cal.diy's native Next.js port. Do not change.
execution_environmentgen2Gen2 recommended; required for NFS and GCS Fuse mounts.
timeout_seconds300Maximum duration per request.
enable_cloudsql_volumetrueCloud SQL Auth Proxy for socket connections.
enable_image_mirroringtrueMirror the Cal.diy image into Artifact Registry.
min_instance_count0Minimum instances (0 = scale-to-zero). Set to 1 to avoid cold-start latency in production.
max_instance_count5Maximum instances.
traffic_split[]Split traffic across revisions for staged rollouts. All entries must sum to 100.

Group 5 — Access & Ingress Control

VariableDefaultDescription
ingress_settingsallWhich networks may reach the service: all, internal, or internal-and-cloud-load-balancing.
vpc_egress_settingPRIVATE_RANGES_ONLYHow outbound traffic is routed through the VPC connector.
enable_iapfalseRequire Google sign-in via Identity-Aware Proxy.
iap_authorized_users / iap_authorized_groups[]Who may access through IAP.

Group 6 — Environment Variables & Secrets

VariableDefaultDescription
environment_variablesSMTP skeletonPlain-text settings. Set SMTP_HOST, SMTP_PORT, SMTP_USER, EMAIL_FROM here. Also set NEXT_PUBLIC_WEBAPP_URL once a custom domain is known.
secret_environment_variables{}Map of env var → Secret Manager secret name. Use for SMTP_PASSWORD.
secret_propagation_delay30Seconds to wait after secret creation before proceeding.
secret_rotation_period2592000sSecret Manager rotation notification period.

Group 7 — Backup & Restore

VariableDefaultDescription
backup_schedule0 2 * * *Automated backup cron (UTC).
backup_retention_days7Retention; raise for production/compliance.
enable_backup_import / backup_source / backup_uri / backup_formatrestore optionsRestore from a backup on deploy. Set false after a successful import.

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 — Custom SQL Scripts

enable_custom_sql_scripts, custom_sql_scripts_bucket, custom_sql_scripts_path, custom_sql_scripts_use_root — run SQL from a GCS bucket after provisioning. See App_CloudRun.

Group 10 — Load Balancer, CDN & Image Retention

VariableDefaultDescription
enable_cloud_armorfalseProvision Global HTTPS LB with Cloud Armor WAF. Required for custom domains and DDoS protection.
admin_ip_ranges[]CIDRs exempted from WAF rules.
application_domains[]Custom hostnames for the external load balancer. When set, also update NEXT_PUBLIC_WEBAPP_URL and NEXTAUTH_URL.
enable_cdnfalseEnable Cloud CDN on the LB backend. Requires enable_cloud_armor.
max_images_to_retain / delete_untagged_images / image_retention_days(set)Artifact Registry cleanup policy.

Group 11 — Storage & Filesystem

VariableDefaultDescription
create_cloud_storagetrueProvision the default data bucket.
storage_buckets[{ name_suffix = "data" }]Additional buckets to provision.
enable_nfsfalseNFS is not required for Cal.diy. Enable only if custom shared storage is needed (requires gen2).
gcs_volumes[]GCS Fuse mounts.
manage_storage_kms_iam / enable_artifact_registry_cmekfalseCMEK options.

Group 12 — Database Backend

VariableDefaultDescription
database_typePOSTGRES_15Fixed — do not change. Cal.diy requires PostgreSQL.
db_namecalcomDatabase name. Immutable after first deploy.
db_usercalcomApplication user. Immutable after first deploy.
database_password_length32Generated password length (16–64).
enable_auto_password_rotation / rotation_propagation_delay_secoffDB password rotation.
db_host_env_var_name / db_name_env_var_name / db_user_env_var_name / db_port_env_var_name / service_url_env_var_name""Alias env var names alongside the standard DB_* variables.

Group 13 — Jobs & Scheduled Tasks

VariableDefaultDescription
initialization_jobs[]Leave empty to use the built-in db-init, db-migrate, and seed-app-store jobs.
cron_jobs[]Recurring Cloud Run Jobs triggered by Cloud Scheduler. Cal.diy does not require scheduled tasks by default.

Group 14 — Observability & Health

VariableDefaultDescription
startup_probeHTTP /api/auth/session, initial_delay=180s, failure_threshold=18Generous window for first-boot start.sh (URL rewrite + migrations + seed).
liveness_probeHTTP /api/auth/session, initial_delay=60sLiveness probe after startup.
uptime_check_configdisabled, path /api/auth/sessionCloud Monitoring uptime check.
alert_policies[]Metric alert policies.

Group 21 — Redis Cache

VariableDefaultDescription
enable_redisfalseUse Redis for session caching. Recommended when max_instance_count > 1.
redis_host""Redis endpoint. Required when enable_redis = true.
redis_port6379Redis port.
redis_auth""Optional Redis auth password (sensitive).

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).
database_instance_nameCloud SQL instance name.
database_name / database_userApplication database name / user.
database_password_secretSecret Manager secret holding the DB password.
database_host / database_portDB endpoint / port.
storage_bucketsCreated Cloud Storage buckets.
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 the setup jobs.
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).

SettingSensible valueRiskConsequence if wrong
database_typePOSTGRES_15CriticalCal.diy requires PostgreSQL with Prisma; MySQL or NONE breaks schema migrations and startup.
container_port3000CriticalCal.diy's Next.js server listens on 3000; any other value misdirects Cloud Run health checks and traffic routing.
enable_cloudsql_volumetrueCriticalCal.diy connects via Unix socket; disabling removes the socket and all DB connections fail.
db_name / db_userset onceCriticalImmutable after first deploy; renaming recreates the DB/user and orphans existing data.
application_versionpinned releaseCriticalcalcom/cal.diy has no latest tag; an invalid version fails the image pull.
NEXT_PUBLIC_WEBAPP_URLmatch public URLCriticalCal.diy embeds this in Next.js static chunks via replace-placeholder.sh; a mismatch breaks OAuth callbacks and booking links.
NEXTAUTH_URLmatch public URLCriticalNextAuth validates OAuth redirect URIs against this; a mismatch blocks all logins.
enable_backup_importfalse unless restoringCriticalEnabling without a valid backup_uri fails the import job and may overwrite live data on subsequent applies.
startup_probe.initial_delay_seconds180 (via CalDiy_Common)HighCal.diy start.sh runs URL rewrite (~2.5 min) + Prisma migrations + seed before serving requests; too short a window causes a restart loop.
startup_probe.failure_threshold18 at period=10sHighGives ~6 minutes total; reducing below 12 kills the container before initialization completes.
container_image_sourcecustomHighCloud Run requires the wrapper image that assembles DATABASE_URL; using prebuilt without the wrapper entrypoint leaves DATABASE_URL unset and all DB queries fail.
memory_limit2Gi minimumHighCal.diy startup (URL rewrite + migrations) requires ≥ 2 GiB; OOM kills before the app is ready.
enable_redistrue for multi-instanceHighWithout Redis, sessions are per-instance; users are logged out when scale-to-zero or instance rotation occurs.
redis_hostrequired when enable_redis=trueHighEmpty redis_host with Redis enabled injects a malformed URL; session operations fail at runtime.
min_instance_count1 for productionMediumScale-to-zero is the default; Cal.diy's 4–5 minute cold start adds unacceptable latency for production scheduling.
SMTP_HOST / EMAIL_FROMreal SMTP configMediumWithout valid SMTP, booking confirmations, reminders, and password resets are never delivered.
vpc_egress_settingPRIVATE_RANGES_ONLYMediumIf using Memorystore Redis, its private IP may not be in default VPC ranges; change to ALL_TRAFFIC or ensure correct VPC routing.
organization_idset explicitly for VPC-SCMediumVPC-SC perimeter is only activated when organization_id is set; enable_vpc_sc = true alone has no effect.
execution_environmentgen2Mediumgen1 does not support NFS mounts; if enable_nfs = true, gen2 is required.

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. Cal.diy-specific application configuration shared with the GKE variant is described in CalDiy_Common.