Skip to main content

OpenEMR GKE Module — Configuration Guide

OpenEMR is a leading open-source electronic health records (EHR) and medical practice management platform used by clinics, hospitals, and healthcare providers worldwide. This module deploys OpenEMR on GKE Autopilot using a custom container image built on Alpine 3.20 with Apache and PHP 8.3 FPM, backed by a managed Cloud SQL MySQL 8.0 instance accessed via a Cloud SQL Auth Proxy sidecar, and a Filestore NFS volume for persistent patient document and sites directory storage.

OpenEMR GKE is a wrapper module built on top of App GKE. It uses App GKE for all GCP infrastructure provisioning (cluster, networking, Cloud SQL, GCS, secrets, CI/CD) and adds OpenEMR-specific application configuration, initialisation jobs, health probes, and runtime defaults on top.

Note: Variables marked as platform-managed are set and maintained by the platform. You do not normally need to change them.


How This Guide Is Structured

This guide documents only the variables that are unique to OpenEMR_GKE or that have OpenEMR-specific defaults that differ from the App_GKE base module. For all other variables — project identity, GKE backend configuration, CI/CD, custom SQL scripts, observability alerting, networking, IAP, and Cloud Armor — refer directly to the App_GKE Configuration Guide.

Variables fully covered by the App GKE guide:

Configuration AreaApp GKE.md SectionOpenEMR-Specific Notes
Module Metadata & Configuration§1 Module OverviewDifferent defaults for module_description and module_documentation. resource_creator_identity behaves identically.
Project & Identity§2 IAM & Access ControlRefer to base App GKE module documentation.
Runtime & Scaling§3.A Compute (GKE Autopilot)See OpenEMR Runtime Configuration below. container_port defaults to 80. session_affinity defaults to "ClientIP".
Environment Variables & Secrets§3.A Compute (GKE Autopilot)See OpenEMR Environment Variables below for PHP and SMTP defaults.
GKE Backend Configuration§3.A Compute (GKE Autopilot)Refer to base App GKE module documentation. session_affinity defaults to "ClientIP". deployment_timeout defaults to 1200.
Jobs & Scheduled Tasks§3.E Initialization Jobs & CronJobsRefer to base App GKE module documentation. The module injects a platform-managed nfs-init initialisation job — see Platform-Managed Behaviours.
CI/CD & GitHub Integration§6 CI/CD & DeliveryRefer to base App GKE module documentation.
Storage — NFS§3.C Storage (NFS / GCS / GCS Fuse)NFS is enabled by default (enable_nfs = true). See NFS & Patient Document Storage below.
Storage — GCS§3.C Storage (NFS / GCS / GCS Fuse)Refer to base App GKE module documentation.
Backup Schedule & Retention§8.B Backup Import & RecoveryRefer to base App GKE module documentation. See also Backup Import & Recovery below.
Custom SQL Scripts§3.E Initialization Jobs & CronJobsRefer to base App GKE module documentation.
Observability & Health§5 Traffic & IngressSee OpenEMR Health Probes below for OpenEMR-specific probe paths and timing defaults.
Reliability Policies§7 Reliability & SchedulingRefer to base App GKE module documentation. enable_pod_disruption_budget defaults to false (because the default max_instance_count = 1 makes a PDB with min_available = 1 block node drains).
Resource Quota§7.C Resource QuotasRefer to base App GKE module documentation.
Custom Domain, Static IP & Network§5.C Static IPRefer to base App GKE module documentation.
Identity-Aware Proxy§4.B Identity-Aware Proxy (IAP)Refer to base App GKE module documentation.
Cloud Armor WAF§4.A Cloud Armor WAFRefer to base App GKE module documentation.
VPC Service Controls§4.D VPC Service ControlsRefer to base App GKE module documentation.
StatefulSet Configuration§3.A Compute (GKE Autopilot)Refer to base App GKE module documentation.
Database Configuration§3.B Database (Cloud SQL)database_type must be "MYSQL_8_0". See OpenEMR Database Configuration below.
Networking & Network Policies§3.D Networking & Network PoliciesRefer to base App GKE module documentation.
Additional Services§3.F Additional ServicesRefer to base App GKE module documentation.
Binary Authorization§4.C Binary AuthorizationRefer to base App GKE module documentation.
Secrets Store CSI§4.E Secrets Store CSIRefer to base App GKE module documentation.
Cloud CDN§5.B Cloud CDNRefer to base App GKE module documentation.
Pod Disruption Budget§7.A Pod Disruption Budgetenable_pod_disruption_budget defaults to false.
Topology Spread§7.B Topology SpreadRefer to base App GKE module documentation.
Auto Password Rotation§7.D Auto Password RotationRefer to base App GKE module documentation.
Redis / Memorystore§8.A Redis / Memorystoreenable_redis defaults to true. See Redis Session Store below.
Service Mesh§8.C Service MeshRefer to base App GKE module documentation.
Multi-Cluster Services§8.D Multi-Cluster ServicesRefer to base App GKE module documentation.

Platform-Managed Behaviours

The following behaviours are applied automatically by OpenEMR GKE regardless of the variable values in your tfvars file. They cannot be overridden by user configuration.

BehaviourDetail
NFS directory initialisationAn nfs-init Kubernetes Job runs automatically on every apply. It mounts the Filestore NFS share, sets ownership of the sites directory to UID 1000 (the Apache process user), downloads and restores a backup if backup_uri is set, and regenerates sqlconf.php with current database credentials. This job must complete before OpenEMR starts.
Cloud SQL Auth Proxy sidecarenable_cloudsql_volume = true is applied unconditionally. A Cloud SQL Auth Proxy container is injected as a sidecar in the same pod. The application connects to MySQL via the Unix socket at 127.0.0.1. This is not configurable by the user.
OE_PASS secretAn OpenEMR admin password is auto-generated by OpenEMR Common and stored in Secret Manager. The plaintext value is passed to App GKE via explicit_secret_values, which creates a Kubernetes Secret and injects it as the OE_PASS environment variable. OpenEMR uses this on first boot to set the administrator account password.
MYSQL_PASS secretThe MySQL database password generated by App GKE is automatically injected as the MYSQL_PASS environment variable via the db_password_env_var_name = "MYSQL_PASS" parameter passed to App GKE. Do not define this manually in secret_environment_variables.
K8S environment variableK8S = "yes" is injected unconditionally into the container. The OpenEMR startup script uses this flag to detect the Kubernetes deployment mode and apply GKE-specific behaviour (such as skipping slow recursive chown operations that would cause startup timeouts).
Network tagsThe network_tags variable defaults to ["nfsserver"]. This tag is required for GKE pod traffic to reach the GCE-based NFS server via VPC firewall rules. Do not remove this tag unless you have replaced the NFS server with Filestore or another solution that does not require it.
BACKUP_FILEID injectionWhen backup_uri is set, it is automatically injected into the nfs-init job as the BACKUP_FILEID environment variable, triggering backup restoration on deployment.

OpenEMR Application Identity

These variables define how the OpenEMR deployment is named across GCP and Kubernetes resources.

VariableDefaultOptions / FormatDescription & Implications
application_name"openemr"[a-z][a-z0-9-]{0,19}Internal identifier used as the base name for the Kubernetes Deployment, Namespace, Cloud SQL database, GCS buckets, and Secret Manager secrets. Functionally identical to application_name in App GKE. Do not change after initial deployment.
display_name"OpenEMR"Any stringHuman-readable name shown in the platform UI and GKE monitoring dashboards. Can be updated freely without affecting resource names.
description"Initialize NFS directories for OpenEMR and restore backup if provided"Any stringDescription used in Kubernetes resource annotations and the nfs-init job. Can be updated freely.
application_version"7.0.4"OpenEMR version string, e.g. "7.0.4", "7.0.3"The OpenEMR release version, used as the container image tag. When container_image_source = "custom", changing this value triggers a new Cloud Build run that builds the specified version.

Validating Application Identity

# Confirm the Deployment exists with the expected name
kubectl get deployments -n NAMESPACE -o wide

# Confirm the running container image tag
kubectl get pods -n NAMESPACE -l app=openemr -o jsonpath='{.items[0].spec.containers[0].image}'

OpenEMR Runtime Configuration

OpenEMR is a PHP/MySQL EHR application with an Apache HTTP server front-end. It has higher memory and ephemeral storage requirements than a typical web service, particularly during the initial database installation and on first boot.

Container Port

VariableDefaultOptions / FormatDescription & Implications
container_port80Integer, 1–65535The port Apache listens on inside the container. OpenEMR's Apache configuration binds to port 80 by default. Do not change this unless you have modified the Apache configuration.

Resource Sizing

The OpenEMR GKE module exposes cpu_limit, memory_limit, and ephemeral_storage_limit as dedicated top-level variables. These are passed into container_resources for the underlying App GKE module.

VariableModule DefaultRecommended for Production
cpu_limit"2000m""2000m" or higher
memory_limit"4Gi""4Gi" (minimum "2Gi")
ephemeral_storage_limit"8Gi""8Gi"

Note on ephemeral storage: OpenEMR writes PHP opcache, Apache logs, session files, and installation temporary files to the container writable layer. The GKE Autopilot default of 1 Gi is insufficient. The Cloud SQL Auth Proxy sidecar consumes 1 Gi of ephemeral storage, leaving a maximum of 9 Gi available for the OpenEMR container within GKE Autopilot's 10 Gi per-pod limit. The "8Gi" default accounts for this headroom.

Recommended production configuration:

cpu_limit              = "2000m"
memory_limit = "4Gi"
ephemeral_storage_limit = "8Gi"

Scaling Defaults

VariableApp GKE DefaultOpenEMR GKE DefaultReason
min_instance_count11OpenEMR should always have at least one running pod to avoid cold starts that impact clinical access.
max_instance_count11OpenEMR's PHP session handling relies on the local NFS mount. Multi-instance deployments require Redis session storage. Increase max_instance_count only after enabling Redis.

Session Affinity

VariableApp GKE DefaultOpenEMR GKE DefaultDescription & Implications
session_affinity"None""ClientIP"Ensures a given client consistently reaches the same pod. This mitigates cross-pod session inconsistency when Redis is not configured. Change to "None" only when Redis session storage is enabled and all pods share session state.

Deployment Timeout

VariableApp GKE DefaultOpenEMR GKE DefaultDescription & Implications
deployment_timeout6001800OpenEMR's initial database installation and PHP asset compilation can take 10–20 minutes on first boot. The extended timeout prevents Terraform from reporting a failure during legitimate long-running first deployments.

Validating Runtime Configuration

# View container resource limits on the running pod
kubectl describe pod -n NAMESPACE -l app=openemr | grep -A15 "Limits:"

# Confirm session affinity on the Service
kubectl get service openemr -n NAMESPACE -o jsonpath='{.spec.sessionAffinity}'

# Confirm the K8S environment variable is set
kubectl exec -n NAMESPACE deploy/openemr -- env | grep "^K8S="

OpenEMR Health Probes

OpenEMR performs database connection validation and, on first boot, runs the full database installation wizard. This startup phase can take 5–20 minutes on a fresh deployment. The startup_probe and liveness_probe variables in OpenEMR GKE have OpenEMR-specific defaults.

VariableDefaultDescription & Implications
startup_probe{ enabled = true, type = "TCP", path = "/", initial_delay_seconds = 0, timeout_seconds = 5, period_seconds = 10, failure_threshold = 12 }Uses a TCP port check on port 80 rather than an HTTP endpoint. A TCP probe is more reliable during OpenEMR's boot phase, when Apache may be accepting connections before PHP and the database are fully ready. With period_seconds = 10 and failure_threshold = 12, Kubernetes allows up to 120 seconds of startup time before declaring the pod unhealthy. On first deployment, consider increasing failure_threshold to 30 or higher to allow for the full database installation.
liveness_probe{ enabled = true, type = "HTTP", path = "/interface/login/login.php", initial_delay_seconds = 0, timeout_seconds = 10, period_seconds = 30, failure_threshold = 10 }Periodically checks that the OpenEMR login page is reachable. The /interface/login/login.php endpoint returns HTTP 200 only when Apache, PHP-FPM, and the database connection are all operational. period_seconds = 30 and failure_threshold = 10 allow up to 5 minutes of recovery time before the pod is restarted.

Probe routing in OpenEMR_GKE: startup_probe and liveness_probe each serve a dual role — they are passed to OpenEMR_Common to configure the Kubernetes container probes, and also forwarded directly to App_GKE (as startup_probe_config and health_check_config respectively) to configure the load balancer backend health checks. Unlike other App_GKE wrapper modules that expose separate startup_probe/liveness_probe variables for container probes and startup_probe_config/health_check_config for LB health checks, OpenEMR_GKE uses the same two variables for both layers. See App_GKE §5 Traffic & Ingress for the App_GKE field reference.

Validating Health Probes

Google Cloud Console: Navigate to Kubernetes Engine → Workloads → openemr and check the Events panel for probe failure messages.

# View probe configuration on the running deployment
kubectl describe deployment openemr -n NAMESPACE | grep -A30 "Liveness:"

# Tail pod logs to monitor startup progress
kubectl logs -n NAMESPACE -l app=openemr --follow | grep -E "apache|php|mysql|openemr"

# Manually test the login page endpoint from within the cluster
kubectl exec -n NAMESPACE deploy/openemr -- curl -s -o /dev/null -w "%{http_code}" http://localhost:80/interface/login/login.php
# Expect: 200

OpenEMR Database Configuration

OpenEMR requires MySQL 8.0. The database is provisioned by the underlying App_GKE module — see App_GKE §3.B for the full variable reference.

The following defaults are OpenEMR-specific and differ from the App GKE defaults:

VariableApp GKE DefaultOpenEMR GKE DefaultRecommendation
db_name"gkeappdb""openemr"The MySQL database created for OpenEMR. Injected as the database name in OpenEMR's sqlconf.php.
db_user"gkeappuser""openemr"The MySQL user for the application. Injected into the OpenEMR configuration.
database_type"POSTGRES""MYSQL_8_0"Must remain MySQL 8.0. OpenEMR does not support PostgreSQL. Setting any other database_type will prevent OpenEMR from starting.

Database connection method: Unlike most App GKE applications that connect via TCP, OpenEMR connects to Cloud SQL via the Cloud SQL Auth Proxy Unix socket mounted at 127.0.0.1. This is enforced by the platform-managed enable_cloudsql_volume = true setting and cannot be changed by the user.

Validating Database Configuration

# Confirm the database and user were created
gcloud sql databases list --instance=INSTANCE_NAME --project=PROJECT_ID

gcloud sql users list --instance=INSTANCE_NAME --project=PROJECT_ID

# Confirm the DB password secret is injected into the running pod
kubectl exec -n NAMESPACE deploy/openemr -- env | grep -E "^MYSQL_PASS"

# Confirm the Cloud SQL Auth Proxy sidecar is running in the pod
kubectl get pod -n NAMESPACE -l app=openemr -o jsonpath='{.items[0].spec.containers[*].name}'

OpenEMR Environment Variables

The environment_variables variable (documented in App_GKE §3.A) can be used to set any PHP or SMTP configuration consumed by the OpenEMR container's startup script.

Commonly configured environment variables:

environment_variables = {
PHP_MEMORY_LIMIT = "512M" # PHP memory limit; increase for large patient datasets
SMTP_HOST = "" # SMTP server for outbound email notifications
SMTP_PORT = "25" # SMTP server port
SMTP_USER = "" # SMTP authentication username
SMTP_PASSWORD = "" # Move sensitive values to secret_environment_variables
SMTP_SSL = "false" # Set to "true" for TLS/SSL SMTP connections
EMAIL_FROM = "openemr@example.com"
}

Configure PHP_MEMORY_LIMIT before going live if your deployment handles large numbers of concurrent patients or generates complex reports. Move sensitive values such as SMTP_PASSWORD to secret_environment_variables:

secret_environment_variables = {
SMTP_PASSWORD = "openemr-smtp-password" # Secret Manager secret name
}

All other environment_variables and secret_environment_variables behaviour is identical to App_GKE — refer to App_GKE §3.A.


NFS & Patient Document Storage

OpenEMR stores patient-uploaded documents, the sites directory configuration, and application state on a shared NFS volume. NFS is enabled by default (enable_nfs = true) because OpenEMR cannot function correctly without persistent shared storage.

VariableDefaultOptions / FormatDescription & Implications
enable_nfstruetrue / falseMust remain true for a functional OpenEMR deployment. Setting to false will prevent the nfs-init job from running and OpenEMR will fail to persist patient data across pod restarts.
nfs_mount_path"/var/www/localhost/htdocs/openemr/sites"Filesystem pathThe path inside the container where the NFS volume is mounted. This maps directly to OpenEMR's sites directory, which contains patient documents, site configuration, and uploaded files. Do not change this unless you have modified the OpenEMR container to use a different sites path.

For the full NFS variable reference (Filestore instance sizing, capacity, etc.), refer to App_GKE §3.C.

Validating NFS Storage

# Confirm the nfs-init job completed successfully
kubectl get jobs -n NAMESPACE -l job-name=nfs-init

# View nfs-init job logs to confirm directory setup and any backup restoration
kubectl logs -n NAMESPACE -l job-name=nfs-init

# Confirm the NFS volume is mounted in the running pod
kubectl exec -n NAMESPACE deploy/openemr -- ls /var/www/localhost/htdocs/openemr/sites

Redis Session Store

OpenEMR supports Redis as a shared PHP session store. Redis is enabled by default (enable_redis = true) because the OpenEMR deployment uses the NFS server's co-located Redis instance by default. When max_instance_count > 1, Redis is required to prevent session loss when requests are routed to different pods. The Redis integration is provided by App_GKE — see §8.A Redis / Memorystore for the full integration reference.

VariableDefaultOptions / FormatDescription & Implications
enable_redistruetrue / falseWhen true, OpenEMR is configured to use Redis for PHP session storage via session.save_handler = redis. The REDIS_SERVER environment variable is set automatically. If redis_host is left blank, the module defaults to using the NFS server's IP address as the Redis host.
redis_host""IP address or hostnameThe Redis server hostname or IP. When left empty and enable_redis = true, the module uses the NFS server IP (which runs a co-located Redis instance). Override with a dedicated Memorystore for Redis instance IP for production deployments requiring higher availability.
redis_port"6379"Port string (e.g. "6379")The port Redis is listening on. Change only if your Redis instance uses a non-standard port.
redis_auth""String (sensitive)Authentication password for the Redis server. Leave empty for the default co-located NFS/Redis configuration. For Memorystore instances with AUTH enabled, set this to the instance's auth string. Treated as sensitive — not stored in plaintext in Terraform state.

Validating Redis Configuration

# Confirm REDIS_SERVER is set in the running pod
kubectl exec -n NAMESPACE deploy/openemr -- env | grep -E "^REDIS"

# Test Redis connectivity from within the pod
kubectl exec -n NAMESPACE deploy/openemr -- redis-cli -h REDIS_HOST -p 6379 PING
# Expect: PONG

Backup Import & Recovery

In addition to the scheduled backup (backup_schedule and backup_retention_days, documented in App_GKE §8.B), OpenEMR_GKE supports a one-time backup restoration during deployment via the nfs-init job. Use this to migrate an existing OpenEMR instance to GCP or to seed a new environment with production data.

VariableDefaultOptions / FormatDescription
enable_backup_importfalsetrue / falseWhen true, triggers a one-time import job after provisioning.
backup_source"gcs"gcs / gdrive"gcs" to import from the automatically created GCS backups bucket; "gdrive" to import from a Google Drive file ID. GCS is recommended for production.
backup_uri""GCS URI or Drive file IDFor GCS: the full object URI (e.g., "gs://my-bucket/backups/openemr.sql"). For Google Drive: the file ID from the share URL. When set, this is automatically injected as BACKUP_FILEID into the nfs-init job.
backup_format"sql"sql / tar / gz / tgz / tar.gz / zipThe format of the backup file. OpenEMR backups are typically MySQL dumps in "sql" or "gz" (gzip-compressed) format.

OpenEMR backup scope: The nfs-init job restores both the MySQL database dump and the NFS sites directory content from the backup archive. The backup should contain the complete OpenEMR sites directory and the MySQL database for a full restoration.

For the full variable reference, refer to App_GKE §8.B.


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

VariableSensible DefaultRiskConsequence of Incorrect Value
database_typeMYSQL_8_0CriticalOpenEMR is MySQL-only. Setting to PostgreSQL causes the initialisation job and all OpenEMR PHP code to fail immediately. The MYSQL_PASS env var and Cloud SQL Auth Proxy socket are hardwired for MySQL protocol.
OE_USER (hardcoded in Common)adminHighHardcoded to admin in the OpenEMR Common init job. The K8S init job creates the admin user and schema under this name. Overriding via environment_variables after first deploy has no effect on the existing account.
OE_PASS (via Secret Manager)Auto-generated 20-character random passwordHighRetrieve from Secret Manager before first login. Once the schema is initialised, the password is stored in the MySQL database — changing the Secret Manager value alone does not update the login password.
K8S (in init job)admin (init job only)CriticalThe init job runs with K8S=admin to execute auto_configure.php and write $config=1 to NFS (sqlconf.php). Changing this value in the init job environment causes auto-setup to be skipped, leaving the database uninitialised. The main pod waits for $config=1 on NFS before starting Apache and will wait indefinitely.
ephemeral_storage_limit"2Gi"CriticalOpenEMR writes PHP opcache, Apache logs, session files, and installation temp files to the container writable layer. GKE Autopilot's default 1Gi ephemeral storage limit is insufficient and causes the pod to be evicted during startup. Must be at least 2Gi. The Cloud SQL Auth Proxy sidecar also consumes 1Gi, so the combined pod limit is 3Gi minimum.
MYSQL_ROOT_PASS (via environment_variables)BLANKHighThe sentinel value BLANK means "no root password" (Cloud SQL Auth Proxy handles authentication). Changing this to a real password causes the init job to attempt direct MySQL root auth, which fails against Cloud SQL and prevents database initialisation.
enable_nfstrue (required)CriticalOpenEMR requires NFS for the sites/ directory, which holds patient documents, configuration, and the sqlconf.php file that signals init completion. Without NFS, the init job cannot write $config=1, the main pod never starts serving, and patient documents cannot be shared across pod restarts.
nfs_mount_path/mnt/nfsHighMust match the path expected by OpenEMR's init scripts and the main container entrypoint. A mismatch causes the init job to write sqlconf.php to a location the main container never checks, and the main container loops waiting for setup completion.
enable_redisfalseMediumWhen enable_redis = true, redis_host must point to a reachable Redis instance. An unreachable host causes PHP session handling to fail, preventing all users from logging in.
redis_host""HighRequired when enable_redis = true. An empty value causes OpenEMR to attempt connection to an empty hostname, producing PHP session errors and login failures for all users.
enable_cloudsql_volumetrueCriticalOpenEMR connects to Cloud SQL via the Auth Proxy Unix socket. Disabling this causes all MySQL connections to fail at startup — the pod enters CrashLoopBackOff immediately.
cpu_limit2000mHighOpenEMR on GKE runs Apache + PHP-FPM with multiple workers. Under concurrent load, insufficient CPU causes PHP-FPM worker timeouts. For production deployments with multiple concurrent clinicians, 2000m is the recommended minimum.
memory_limit2GiHighOpenEMR PDF generation, billing reports, and patient record loading are memory-intensive. Less than 1Gi causes OOM kills mid-request; 2Gi is the recommended minimum for reliable operation.
min_instance_count1HighScale-to-zero is inappropriate for a clinical EHR. A cold start takes 20–40 seconds; a clinician attempting to access a patient record during that window sees an error that could be mistaken for a system failure. Keep at least 1 replica running.
quota_memory_requests / quota_memory_limits"4Gi" / "8Gi"CriticalMust use binary unit suffixes (e.g., "4Gi", "8192Mi"). Bare integers are treated as bytes by Kubernetes, creating a near-zero memory quota that immediately blocks all pod scheduling.
backup_schedule"0 2 * * *"CriticalAn empty string disables automated backups. OpenEMR contains protected health information (PHI). Without backups, a Cloud SQL failure results in permanent loss of patient records — a HIPAA compliance violation.
backup_retention_days7MediumFor HIPAA-regulated environments, retain at least 90 days of backups. Setting to 0 or 1 leaves an unacceptably narrow recovery window.
enable_pod_disruption_budgettrueHighDisabling PDB allows GKE node upgrades to evict all OpenEMR pods simultaneously, causing a full EHR service outage during maintenance windows. For a clinical system, this is unacceptable.
stateful_pvc_enabledfalseMediumOpenEMR uses NFS (not PVCs) for shared persistent storage. Enabling PVCs adds unused local disk, wastes resources, and can block pod rescheduling when GKE Autopilot cannot provision the requested disk type.
enable_vpc_scfalseHighRequires explicit organization_id. For HIPAA environments, VPC Service Controls prevent data exfiltration. Enabling without a valid org ID silently skips perimeter creation, giving a false sense of security.
enable_auto_password_rotationfalseMediumWhen enabled, the MySQL password rotates on schedule. The OpenEMR pod must be restarted after rotation; otherwise it continues using the old (now invalid) password until connections fail and the pod enters CrashLoopBackOff.

Deployment Prerequisites & Validation

After deploying OpenEMR GKE, confirm the deployment is healthy:

# Confirm the nfs-init job completed successfully
kubectl get jobs -n NAMESPACE

# View nfs-init job logs to confirm storage preparation and backup restoration
kubectl logs -n NAMESPACE -l job-name=nfs-init

# Confirm the OpenEMR pod is running and healthy
kubectl get pods -n NAMESPACE -l app=openemr

# Retrieve the external IP of the OpenEMR LoadBalancer service
kubectl get svc -n NAMESPACE

# Test the OpenEMR login page (replace EXTERNAL_IP)
curl -s -o /dev/null -w "%{http_code}" http://EXTERNAL_IP/interface/login/login.php
# Expect: 200

# Confirm the OE_PASS admin password secret was created in Secret Manager
gcloud secrets list --project=PROJECT_ID --filter="name:openemr" | grep password

# Confirm the Cloud SQL Auth Proxy sidecar is running alongside OpenEMR
kubectl get pod -n NAMESPACE -l app=openemr -o jsonpath='{.items[0].status.containerStatuses[*].name}'