Skip to main content

Certification track: Associate Cloud Engineer (ACE)

OpenEMR on Google Cloud Run

OpenEMR on Google Cloud Run

OpenEMR is the world's most widely adopted open-source Electronic Health Records (EHR) and practice management system, used by 100,000+ healthcare providers across 100+ countries. This module deploys OpenEMR 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 OpenEMR 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

OpenEMR runs as an Apache/PHP 8.3 FPM container on Cloud Run v2. The deployment wires together a focused set of Google Cloud services:

CapabilityGoogle Cloud serviceNotes
ComputeCloud Run v2Apache/PHP service, 2 vCPU / 4 GiB by default, request-based autoscaling
DatabaseCloud SQL for MySQL 8.0Required — OpenEMR does not support PostgreSQL
Patient documentsFilestore (NFS)sites/ directory with patient documents, session cache, and application state shared across all instances (gen2 required)
Object storageCloud StorageA general-purpose data bucket
Session storeRedisEnabled by default; falls back to the NFS server IP when no Redis host is given
SecretsSecret ManagerAuto-generated admin password (OE_PASS) and database password (MYSQL_PASS)
IngressCloud Run URL / Cloud Load BalancingDefault run.app URL, optional external HTTPS load balancer + custom domain

Sensible defaults worth knowing up front:

  • MySQL 8.0 is mandatory. Selecting PostgreSQL or NONE breaks startup.
  • NFS is mandatory and requires gen2. OpenEMR's sites/ directory — containing sqlconf.php, patient documents, Twig/Smarty caches, and uploaded files — must be on a shared NFS volume mounted via the Cloud Run gen2 execution environment.
  • The startup probe is TCP, not HTTP. Cloud Run health traffic arrives over plain HTTP. OpenEMR's Apache/PHP stack may not yet be serving HTTP during the first-boot installation phase, so an HTTP probe would time out. A TCP probe checks only that the port is open and allows the installer to complete.
  • First-boot installation is automated and slow. On first deploy, two initialization jobs run — nfs-init (NFS directory setup and optional backup restore) and db-init (MySQL user and database creation) — after which the container itself runs auto_configure.php to install the database schema. This can take 5–20 minutes.
  • The OpenEMR admin password is generated automatically and stored in Secret Manager; you never set it in plain text.
  • min_instance_count defaults to 1. Scale-to-zero is not recommended for clinical EHR systems — cold starts add latency that clinicians may interpret as a system failure.
  • max_instance_count defaults to 1. Increase only after confirming Redis session sharing is operational; multiple instances without Redis cause PHP session loss.

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 OpenEMR service

OpenEMR runs as a Cloud Run v2 service. Each deployment creates an immutable revision; traffic can be split across revisions for staged rollouts. The service requires the gen2 execution environment for NFS volume support.

  • 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 MySQL 8.0

OpenEMR stores all clinical data in a managed Cloud SQL for MySQL 8.0 instance. The service connects privately through the Cloud SQL Auth Proxy over a Unix socket (no public IP). On first deploy the db-init Cloud Run job creates the application database and user; the nfs-init job prepares the NFS sites/ directory.

  • 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> --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. Filestore (NFS) and Cloud Storage

OpenEMR's sites/ directory is written to a Filestore (NFS) share mounted into the service at /var/www/localhost/htdocs/openemr/sites. This directory contains sqlconf.php (which signals installation completion), patient-uploaded documents, and Twig/Smarty template caches. All instances must share the same NFS mount. A general-purpose Cloud Storage bucket is also provisioned.

  • Console: Filestore → Instances; Cloud Storage → Buckets.
  • CLI:
    gcloud filestore instances list --project "$PROJECT"
    gcloud storage buckets list --project "$PROJECT"
    # Inspect the nfs-init job execution logs:
    gcloud run jobs executions list --job nfs-init --project "$PROJECT" --region "$REGION"

See App_CloudRun for the NFS mount, GCS Fuse, and CMEK.

D. Redis session store

Redis backs OpenEMR's PHP session store. When redis_host is left empty and NFS is enabled, the NFS server's co-located Redis instance is used automatically. In multi-instance deployments, a shared session store is required to prevent session loss.

  • Console: Memorystore → Redis (if using a managed Memorystore instance).
  • CLI:
    redis-cli -h <redis-host> ping
    redis-cli -h <redis-host> info keyspace

E. Secret Manager

The OpenEMR admin password (OE_PASS) and the MySQL database password (MYSQL_PASS) are stored in Secret Manager and injected into the service at runtime. Plaintext never appears in configuration.

  • Console: Security → Secret Manager.
  • CLI:
    gcloud secrets list --project "$PROJECT"
    # Retrieve the admin password to log in for the first time:
    gcloud secrets versions access latest \
    --secret=<admin_password_secret_id> --project "$PROJECT"

The admin password secret ID is exposed as the admin_password_secret_id output. See App_CloudRun for injection and rotation details.

F. 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 top. Ingress settings and VPC egress control traffic to/from the service.

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

G. 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. OpenEMR Application Behaviour

  • Two initialization jobs run on every deploy.

    JobPurposeImage
    nfs-initPrepares the NFS sites/ directory structure, sets ownership to UID 1000 (Apache), and optionally restores a backup when backup_uri is setgoogle-cloud-cli:alpine
    db-initCreates the MySQL database and application usermysql:8.0-debian

    Inspect the jobs and their executions:

    gcloud run jobs list --project "$PROJECT" --region "$REGION"
    gcloud run jobs executions list --job nfs-init --project "$PROJECT" --region "$REGION"
    gcloud run jobs executions list --job db-init --project "$PROJECT" --region "$REGION"
  • First-boot schema installation takes 5–20 minutes. After the init jobs complete, the service container runs auto_configure.php to install the OpenEMR database schema and create the admin account. During this phase a temporary PHP built-in web server serves HTTP 200 on the startup probe path, preventing the instance from being killed while the installer runs.

  • Startup probe is TCP. The Cloud Run startup probe defaults to TCP on port 80 to avoid false failures during the first-boot installation phase when Apache/PHP may not yet be serving HTTP responses.

  • Version-aware upgrades. On subsequent deployments the startup script compares the image version against the NFS-stored version and runs the appropriate upgrade scripts (fsupgrade-N.sh) automatically.

  • Admin login. The initial administrator username is admin. The password is auto-generated and stored in Secret Manager — retrieve it with:

    gcloud secrets versions access latest \
    --secret=<admin_password_secret_id> --project "$PROJECT"

    If the admin account is locked after failed login attempts, use the /root/unlock_admin.sh <new_password> utility from inside the running container.

  • HIPAA considerations. OpenEMR stores Protected Health Information (PHI). For HIPAA-regulated deployments, enable enable_iap or enable_cloud_armor to restrict access, set enable_audit_logging = true, and raise backup_retention_days to at least 90.


4. Configuration Variables

Variables are grouped exactly as they appear on the deployment platform. Only settings specific to or notable for OpenEMR 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_nameopenemrBase name for resources. Do not change after first deploy.
display_nameOpenEMRFriendly name shown in the Console.
description(set)Service description.
application_version7.0.4OpenEMR image version tag; increment to deploy a new release.

Group 4 — Runtime & Scaling

VariableDefaultDescription
deploy_applicationtrueSet false to provision infrastructure only without deploying the container.
cpu_limit2000mCPU per instance; 2 vCPU recommended for concurrent clinical workloads.
memory_limit4GiMemory per instance; 4 GiB recommended. Below 2 GiB causes OOM kills under clinical load.
min_instance_count1Minimum instances. Keep ≥ 1 to avoid cold-start delays for clinical users.
max_instance_count1Increase only after confirming Redis session sharing is operational.
container_port80OpenEMR/Apache listens on port 80.
execution_environmentgen2Must remain gen2 for NFS volume support.
timeout_seconds300Max request duration. Increase for report generation or large file uploads.
traffic_split[]Split traffic across revisions for staged rollouts.
max_revisions_to_retain7How many old revisions to keep.

Group 5 — Access & Ingress Control

VariableDefaultDescription
ingress_settingsallWhich networks may reach the service. Use internal-and-cloud-load-balancing for HIPAA deployments fronted by a load balancer.
vpc_egress_settingPRIVATE_RANGES_ONLYHow outbound traffic is routed through the VPC connector.
enable_iapfalseRequire Google sign-in via Identity-Aware Proxy. Recommended for clinical-staff-only access.
iap_authorized_users / iap_authorized_groups[]Who may access through IAP.

Group 6 — Environment Variables & Secrets

VariableDefaultDescription
environment_variables{}Extra non-secret settings. Core MYSQL_* and OE_* values are set automatically. Common additions: PHP_MEMORY_LIMIT, SMTP_HOST, SMTP_PORT.
secret_environment_variables{}Map of env var → Secret Manager secret name. Use for sensitive values such as SMTP credentials.
secret_propagation_delay / secret_rotation_period(set)Replication wait / rotation cadence.

Group 7 — Backup & Restore

VariableDefaultDescription
backup_schedule0 2 * * *Automated backup cron (UTC). Do not disable for HIPAA-regulated deployments.
backup_retention_days7Retention; raise to 30–90 for production/compliance.
enable_backup_importfalseRestore from a backup on deploy.
backup_sourcegcsImport source: gcs or gdrive.
backup_uri""GCS URI (gs://bucket/path) or Google Drive file ID. When set, injected into nfs-init as BACKUP_FILEID.
backup_formatsqlBackup file format: sql, tar, gz, tgz, tar.gz, or zip.

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, binauthz_evaluation_mode.

Group 9 — NFS Instance & Custom SQL

VariableDefaultDescription
nfs_instance_name / nfs_instance_base_name(set)Existing NFS instance / base name for an inline one.
enable_custom_sql_scripts / custom_sql_scripts_bucket / custom_sql_scripts_path / custom_sql_scripts_use_rootoffRun SQL from a GCS bucket after provisioning.

Group 10 — Domain, CDN, Cloud Armor & Image Retention

VariableDefaultDescription
enable_cloud_armorfalseProvision Global HTTPS LB + Cloud Armor WAF.
application_domains[]Custom hostnames for the HTTPS load balancer.
enable_cdnfalseEnable Cloud CDN on the LB backend.
admin_ip_ranges[]CIDRs allowed privileged access.
max_images_to_retain / delete_untagged_images / image_retention_days(set)Artifact Registry cleanup policy.

Group 11 — Storage & Filesystem

VariableDefaultDescription
enable_nfstrueMust remain true. OpenEMR requires NFS for the sites/ directory.
nfs_mount_path/var/www/localhost/htdocs/openemr/sitesMount path. Must match the OpenEMR sites directory.
create_cloud_storage / storage_buckets / gcs_volumes(set)Data bucket / additional buckets / GCS Fuse mounts.
manage_storage_kms_iam / enable_artifact_registry_cmekfalseCMEK options.

Group 12 — Database Backend

VariableDefaultDescription
database_typeMYSQL_8_0Fixed — do not change. OpenEMR requires MySQL.
db_nameopenemrDatabase name. Immutable after first deploy.
db_useropenemrApplication 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""Additional env var names under which connection details are injected.

Group 13 — Jobs & Scheduled Tasks

VariableDefaultDescription
initialization_jobs[]Leave empty to use the built-in nfs-init / db-init sequence.
cron_jobs[]Recurring Cloud Run jobs invoked on a schedule.

Group 14 — Observability & Health

VariableDefaultDescription
startup_probeTCP on port 80, 12 failures × 10sTCP startup probe. Avoids HTTP probe failures during the first-boot installation phase.
liveness_probeHTTP GET /interface/login/login.php, 10 failures × 30sLogin page returns HTTP 200 only when the full stack is operational.
uptime_check_configdisabledCloud Monitoring uptime check. Enable explicitly once the service is reachable.
alert_policies[]Metric alert policies.

Group 21 — Redis Session Store

VariableDefaultDescription
enable_redistrueUse Redis for PHP session storage.
redis_host""Leave empty to use the NFS server IP; set explicitly for a dedicated Memorystore instance.
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 to be set explicitly. Recommended for HIPAA environments.
vpc_cidr_ranges / vpc_sc_dry_run(set)Access level CIDRs / dry-run mode.
enable_audit_loggingfalseDetailed Cloud Audit Logs (DATA_READ, DATA_WRITE). Recommended for HIPAA compliance.

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).
admin_password_secret_idSecret Manager secret ID for the OpenEMR admin password (OE_PASS).
database_instance_nameCloud SQL instance name.
database_name / database_userApplication database name / user.
database_password_secretSecret Manager secret holding the DB password (MYSQL_PASS).
database_host / database_portDB endpoint / port.
nfs_server_ipInternal IP of the NFS server (sensitive).
nfs_instance_tagsNetwork tags of the NFS instance.
nfs_mount_pathNFS mount path inside the container.
nfs_share_pathNFS share path on the server.
nfs_setup_jobName of the NFS setup job.
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
enable_nfstrueCriticalOpenEMR cannot function without NFS. The sites/ directory, sqlconf.php, and patient documents all live on NFS. Disabling causes immediate startup failure.
nfs_mount_path/var/www/localhost/htdocs/openemr/sitesCriticalMust match the OpenEMR sites directory path. A mismatch means nfs-init prepares the wrong location and the container never finds a configured sqlconf.php.
execution_environmentgen2Criticalgen1 does not support NFS mounts; the service fails to start.
database_typeMYSQL_8_0CriticalOpenEMR requires MySQL; PostgreSQL or NONE breaks the installer and all PHP database calls.
db_name / db_userset onceCriticalImmutable after first deploy; renaming recreates the DB/user and destroys all patient data.
enable_backup_importfalse unless restoringCriticalEnabling without a valid backup_uri fails the import job and can corrupt the NFS sites directory.
backup_schedule0 2 * * *CriticalDisabling backups for an EHR containing PHI is a HIPAA compliance violation.
startup_probeTCP (default)HighAn HTTP probe fails during the first-boot installation phase when Apache has not yet started fully, causing Cloud Run to restart the container before setup completes.
enable_redistrueHighMultiple instances with isolated PHP session stores cause session loss and login failures for clinical users.
redis_host"" (NFS) or explicitHighAn unreachable Redis host causes PHP session failures and prevents all logins.
memory_limit4GiHighOpenEMR PDF generation and billing reports are memory-intensive. Below 2 GiB causes OOM kills mid-request.
min_instance_count1HighScale-to-zero adds cold-start latency and risks missed clinical access.
backup_retention_days7 (raise for prod)MediumHIPAA-regulated environments should retain at least 90 days.
enable_iap / enable_cloud_armorenable for healthcareMediumThe OpenEMR admin interface and patient records are publicly reachable without these controls.
enable_audit_loggingtrue for HIPAAMediumHIPAA requires audit logging of access to PHI.

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