Production-ready deployment of OpenEMR 8.0.0 on Red Hat OpenShift Developer Sandbox using a custom CentOS 10 Stream container with PHP 8.5 from Remi's repository.
This project provides a complete containerized deployment of OpenEMR (Open-source Electronic Medical Records) on Red Hat OpenShift Developer Sandbox.
| Component | Version | Purpose |
|---|---|---|
| OpenEMR | 8.0.0 | Electronic Medical Records System |
| PHP | 8.5 | Runtime (Remi's Repository) |
| MariaDB | 11.8 | Database Backend |
| Redis | 8 Alpine | Session Storage & Cache |
| nginx | 1.26 | Web Server |
| CentOS Stream | 10 | Base Container OS |
| Node.js | 22 LTS | Frontend Build (build-time only) |
- Custom OpenEMR Container: Built on CentOS 10 Stream with Remi's PHP 8.5
- Redis Session Storage: Redis 8 Alpine for improved performance and scalability
- MariaDB 11.8: Latest Fedora MariaDB for robust database backend
- Developer Sandbox Ready: Optimized for Developer Sandbox storage and resource constraints
- OpenShift Native: Designed for OpenShift SCCs and security constraints
- Production Ready: Includes health checks, resource limits, and monitoring
- HIPAA Considerations: Encrypted transport, audit logging capabilities
- Auto-Configuration: Zero-touch deployment with automated setup
- US Core 8.0 / USCDI v5: Latest FHIR interoperability standards
Note: This deployment is configured for OpenShift Developer Sandbox which uses AWS EBS storage (ReadWriteOnce only). OpenEMR runs as a single replica, suitable for development, demo, and small practice environments.
OpenEMR stands as the world's most popular open-source electronic health records and medical practice management solution, and for good reason:
Certified Excellence: OpenEMR 7.0 achieved ONC 2015 Cures Update Certification, meeting rigorous U.S. federal standards for interoperability, security, and clinical quality measures. This certification enables providers to participate in Quality Payment Programs (QPP/MIPS) and demonstrates commitment to healthcare standards.
Global Impact at Scale: With over 100,000 medical providers serving more than 200 million patients across 100+ countries, OpenEMR has proven its reliability in diverse healthcare settings. The software is translated into 36 languages and downloaded 2,500+ times monthly, reflecting its worldwide trust and adoption.
True Interoperability: OpenEMR implements modern healthcare standards including FHIR APIs, SMART on FHIR, OAuth2, CCDA, Direct messaging, and Clinical Quality Measures (eCQMs). This extensive interoperability enables seamless integration with labs, hospitals, health information exchanges, and third-party applications—eliminating data silos and vendor lock-in.
Cost-Effective Freedom: As genuinely free and open-source software (no licensing fees, ever), OpenEMR provides an economically sustainable alternative to proprietary systems. Healthcare organizations maintain complete control over their data and infrastructure, with the freedom to customize, extend, or migrate without vendor restrictions or hidden costs.
Community-Driven Innovation: Developed since 2002 by physicians for physicians, OpenEMR benefits from contributions by hundreds of developers and support from 40+ professional companies. This vibrant ecosystem ensures continuous improvement, long-term sustainability, and responsive support options ranging from community forums to professional vendors.
Healthcare Without Boundaries: OpenEMR's mission ensures that quality healthcare technology remains accessible regardless of practice size, geographic location, or economic resources. This democratization of healthcare IT particularly benefits underserved communities, small practices, and international healthcare providers who were left behind by commercial EHR systems.
Whether you're a solo practitioner, a community health center, or a large healthcare system, OpenEMR provides enterprise-grade capabilities without enterprise-grade costs—proving that world-class healthcare software should be accessible to all.
┌─────────────────────────────────────────────┐
│ OpenShift Route (HTTPS) │
│ openemr-openemr.apps.sandbox.xxx.xxx │
└─────────────────┬───────────────────────────┘
│
┌─────────────────▼───────────────────────────┐
│ OpenEMR Service (ClusterIP) │
│ Port 8080 │
└─────────────────┬───────────────────────────┘
│
┌─────────▼──────────┐
│ OpenEMR Pod │
│ (single replica) │
│ nginx + PHP-FPM │
└────┬───────────┬───┘
│ │
┌────────▼───┐ ┌───▼──────────┐
│ Redis Svc │ │ MariaDB Svc │
│ Port 6379 │ │ Port 3306 │
└────┬───────┘ └───┬──────────┘
│ │
┌────▼────────┐ ┌──▼───────────┐
│ Redis Pod │ │ MariaDB │
│ (sessions) │ │ StatefulSet │
└────┬────────┘ └──┬───────────┘
│ │
┌────▼────────┐ ┌─▼────────────┐
│ Redis PVC │ │ Database PVC │
│ (RWO - 1Gi) │ │ (RWO - 5Gi) │
└─────────────┘ └──────────────┘
┌─────────────────┐
│ Documents PVC │
│ (RWO - 10Gi) │
│ gp3 storage │
└─────────────────┘
Developer Sandbox Constraints:
- AWS EBS storage (gp3) provides ReadWriteOnce (RWO) volumes only
- Single OpenEMR replica due to RWO storage limitation
- Resource quotas: ~768Mi RAM and ~500m CPU per container
- Total storage: 16Gi (5Gi database + 10Gi documents + 1Gi Redis)
- Redis 8 Alpine for PHP session storage
- Base: CentOS 10 Stream
- PHP: 8.5 (from Remi's repository)
- Web Server: nginx + PHP-FPM (via supervisord)
- OpenEMR: 8.0.0
- Session Storage: Redis (tcp://redis:6379)
- Features:
- OpenShift SCC compliant (runs as arbitrary UID)
- Health check endpoints
- OPcache enabled for performance
- All required PHP extensions
- Redis session handler for scalability
- Image: Redis 8 Alpine (docker.io/redis:8-alpine)
- Storage: 1Gi RWO persistent volume (gp3)
- Purpose: PHP session storage
- Configuration:
- maxmemory: 256MB with LRU eviction policy
- Persistence: AOF (Append Only File)
- Non-root execution (OpenShift restricted SCC)
- Image: Fedora MariaDB 11.8 (quay.io/fedora/mariadb-118)
- Storage: 5Gi RWO persistent volume (gp3)
- Credentials: Auto-generated secure passwords
- Documents: 10Gi RWO volume (for patient documents, images) - gp3 EBS
- Database: 5Gi RWO volume (for MariaDB data) - gp3 EBS
- Redis: 1Gi RWO volume (for session persistence) - gp3 EBS
- Storage Class:
gp3(AWS EBS CSI driver, default in Developer Sandbox) - Total: 16Gi
- Red Hat OpenShift Developer Sandbox account (Get free access)
ocCLI tool installed and configured- Access to Quay.io for pulling container images (or build your own)
- Basic understanding of Kubernetes/OpenShift concepts
Developer Sandbox Limitations to be aware of:
- Projects expire after 30 days of inactivity
- Storage limited to ~40GB total per namespace
- Resource quotas: Limited CPU/memory per namespace
- No cluster-admin access
- Single replica deployments recommended for persistent storage
git clone https://github.com/ryannix123/openemr-openshift.git
cd openemr-openshiftIf you want to build your own container:
# Build the container (creates both :latest and :8.0.0 tags)
podman build -t quay.io/ryan_nix/openemr-openshift:latest .
# Push to Quay.io
podman login quay.io
podman push quay.io/ryan_nix/openemr-openshift:latestOr use the pre-built image: quay.io/ryan_nix/openemr-openshift:latest
The script is pre-configured for Developer Sandbox with sensible defaults:
- Storage:
gp3(default Developer Sandbox storage class) - Database: 5Gi
- Documents: 10Gi
You can optionally adjust these in deploy-openemr.sh if needed, but defaults work well for most cases.
# Get your login command from the Developer Sandbox web console
oc login --token=sha256~xxxxx --server=https://api.sandbox.xxxxx.openshiftapps.com:6443chmod +x deploy-openemr.sh
./deploy-openemr.shThe script will:
- Create the OpenShift project
- Deploy MariaDB with persistent storage
- Deploy OpenEMR application
- Create routes for external access
- Display access credentials
- Navigate to the URL provided in the deployment summary
- Follow the OpenEMR setup wizard
- Use the database credentials from
openemr-credentials.txt
The deployment uses AWS EBS gp3 storage (default in Developer Sandbox):
- Access Mode: ReadWriteOnce (RWO) only
- Storage Class:
gp3(default) - Available: gp2, gp2-csi, gp3, gp3-csi (all RWO)
- Not Available: ReadWriteMany (RWX) storage
Note: Due to RWO storage limitations, OpenEMR runs as a single replica. This is suitable for development, testing, and small practice environments.
Important: Scaling to multiple replicas is not supported with RWO storage. If you need high availability:
- Deploy on a full OpenShift cluster with RWX storage (e.g., ODF CephFS)
- Update storage class to RWX-capable storage
- Change
ReadWriteOncetoReadWriteManyin documents PVC - Then scale:
oc scale deployment/openemr --replicas=3 -n openemr
Current resource allocations (optimized for Developer Sandbox):
OpenEMR Pod:
- Requests: 384Mi RAM, 200m CPU
- Limits: 768Mi RAM, 500m CPU
MariaDB:
- Requests: 512Mi RAM, 200m CPU
- Limits: 1Gi RAM, 500m CPU
Redis:
- Requests: 128Mi RAM, 100m CPU
- Limits: 256Mi RAM, 250m CPU
Total Namespace Usage:
- RAM: ~1Gi requests, ~2Gi limits
- CPU: ~500m requests, ~1250m limits
- Storage: 16Gi (5Gi DB + 10Gi documents + 1Gi Redis)
These values fit within typical Developer Sandbox namespace quotas.
The container includes these PHP settings optimized for OpenEMR:
upload_max_filesize = 128M
post_max_size = 128M
memory_limit = 512M
max_execution_time = 300
# Session storage via Redis
session.save_handler = redis
session.save_path = "tcp://redis:6379"All required OpenEMR extensions are included:
- php-mysqlnd (database)
- php-gd (image processing)
- php-xml (XML processing)
- php-mbstring (multi-byte strings)
- php-zip (compression)
- php-curl (HTTP requests)
- php-opcache (performance)
- php-ldap (LDAP authentication)
- php-soap (web services)
- php-imap (email)
- php-sodium (encryption)
- php-pecl-redis5 (session storage)
The container exposes these endpoints:
/health- General health check (returns 200)/fpm-status- PHP-FPM status page
# OpenEMR application logs
oc logs -f deployment/openemr -n openemr
# MariaDB logs
oc logs -f statefulset/mariadb -n openemr
# Get all pods
oc get pods -n openemrPod not starting:
# Describe the pod for events
oc describe pod <pod-name> -n openemr
# Check for image pull errors
oc get events -n openemr --sort-by='.lastTimestamp'Storage issues:
# Check PVC status
oc get pvc -n openemr
# Describe PVC for binding issues
oc describe pvc <pvc-name> -n openemrDatabase connection errors:
# Verify MariaDB is running
oc get pods -l app=mariadb -n openemr
# Test database connectivity from OpenEMR pod
oc exec -it deployment/openemr -n openemr -- bash
# Inside the pod:
php -r "mysqli_connect('mariadb', 'openemr', 'password', 'openemr') or die(mysqli_connect_error());"To completely remove and redeploy:
oc delete project openemr
# Wait for project to fully delete, then re-run:
./deploy-openemr.shThis deployment includes several security features for healthcare environments:
- Encryption in Transit: TLS/HTTPS via OpenShift routes
- Encryption at Rest: Enable encrypted storage classes
- Access Controls: Leverage OpenShift RBAC
- Audit Logging: OpenEMR's built-in audit log
- Network Policies: Implement NetworkPolicy objects
For production healthcare deployments:
-
Enable Encryption at Rest:
# Use encrypted storage classes STORAGE_CLASS="ocs-storagecluster-ceph-rbd-encrypted"
-
Implement Network Policies:
# Deny all traffic except necessary connections kind: NetworkPolicy apiVersion: networking.k8s.io/v1 metadata: name: openemr-netpol spec: podSelector: matchLabels: app: openemr policyTypes: - Ingress - Egress ingress: - from: - namespaceSelector: matchLabels: name: openshift-ingress egress: - to: - podSelector: matchLabels: app: mariadb ports: - protocol: TCP port: 3306
-
Configure Backup Strategy:
# Use OADP or Velero for backup/restore # Schedule regular database backups
-
Enable Pod Security Standards:
oc label namespace openemr \ pod-security.kubernetes.io/enforce=restricted \ pod-security.kubernetes.io/warn=restricted
The service-mesh/ sub-project adds a zero-trust networking layer using OpenShift Service Mesh 3 (OSSM 3) in ambient mode. This is an optional but strongly recommended addition for any environment handling real patient data.
The base deployment secures the perimeter — TLS on the route, resource isolation via namespaces — but by default, pod-to-pod traffic inside the cluster is unencrypted and unrestricted. Any workload that gains a foothold in the openemr namespace can freely connect to MariaDB or Redis and sniff credentials or patient data in transit. The service mesh closes this gap.
| Layer | What It Does |
|---|---|
| Automatic mTLS (ztunnel) | All pod-to-pod traffic is encrypted and mutually authenticated without any changes to OpenEMR, MariaDB, or Redis |
| Identity-based AuthorizationPolicies | Only OpenEMR's service account identity can reach MariaDB (port 3306) and Redis (port 6379) — no other pod can connect regardless of IP |
| Waypoint Proxy | Enforces L7 policies via an Envoy-based proxy deployed per namespace, required for fine-grained HTTP-level controls |
| NetworkPolicies | L3/L4 isolation enforced by OVN-Kubernetes independent of the mesh — defense in depth |
| EgressFirewall | Pods may only initiate outbound connections to an explicit allow-list; a compromised pod cannot phone home |
OSSM 3 uses a fundamentally different architecture. Instead of injecting an Envoy sidecar into every pod (which requires pod restarts and shows as 2/2 containers), ambient mode deploys a ztunnel DaemonSet that intercepts traffic at the Linux network namespace level on each node. Pods remain 1/1 and are enrolled simply by labeling the namespace — no rollout required. A separate waypoint proxy handles L7 policy enforcement only where needed.
Requirement: OSSM 3 requires cluster-admin access to install the Sail Operator and the
IstioCNIDaemonSet. It is not compatible with the Developer Sandbox. Use a full OpenShift cluster or Single Node OpenShift (SNO).
cd service-mesh/
chmod +x deploy-mesh.sh
# Full install: operators, control plane, policies, Kiali
./deploy-mesh.sh --full
# Or step by step — useful if OpenEMR is already deployed:
./deploy-mesh.sh --operators # Install Sail + Kiali operators, Gateway API CRDs
./deploy-mesh.sh --control-plane # Deploy Istio + ztunnel
./deploy-mesh.sh --policies # Enroll namespace, waypoint, AuthZ, NetworkPolicy, Egress
# Check status at any point
./deploy-mesh.sh --statusSee service-mesh/README.md for the full deployment guide, manifest reference, HIPAA alignment table, and troubleshooting steps.
openemr-on-openshift/
├── Containerfile # Container build instructions
├── deploy-openemr.sh # Automated deployment script
├── README.md # This file
├── .containerignore # Files to ignore during build
├── manifests/ # Individual YAML manifests
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── route.yaml
│ └── mariadb/
│ ├── statefulset.yaml
│ └── service.yaml
└── service-mesh/ # Zero-trust networking sub-project (OSSM 3)
├── README.md # Service mesh deployment guide
├── deploy-mesh.sh # Operator + mesh deployment script
└── manifests/
├── 00-sail-operator.yaml # Sail Operator subscription (OLM)
├── 00-kiali-operator.yaml # Kiali Operator subscription + Kiali CR
├── 01-istio.yaml # Istio CR (ambient profile)
├── 02-istiocni.yaml # IstioCNI CR (ztunnel DaemonSet)
├── 03-namespace.yaml # Namespace with ambient enrollment label
├── 04-waypoint.yaml # Waypoint proxy (L7 policy enforcement)
├── 05-authz-policies.yaml # AuthorizationPolicies (default-deny + allows)
├── 06-network-policies.yaml # NetworkPolicies (L3/L4 CNI isolation)
└── 07-egress-firewall.yaml # EgressFirewall (OVN-Kubernetes)
Contributions are welcome! Areas for improvement:
- Helm chart version
- GitOps/ArgoCD manifests
- Automated database migrations
- Prometheus metrics exporters
- Custom Operator
- Multi-tenancy support
- OpenEMR Official Site
- OpenEMR Documentation
- Red Hat OpenShift Documentation
- OpenShift Service Mesh 3 Documentation
- OpenShift Data Foundation
This project follows OpenEMR's licensing. OpenEMR is licensed under GPL v3.
Ryan Nix
- Senior Solutions Architect, Red Hat
- GitHub: @ryannix123
- Quay.io: ryan_nix
- OpenEMR development team
- Red Hat OpenShift team
- Based on the Nextcloud on OpenShift pattern
Note: This is designed for healthcare environments. Ensure compliance with HIPAA, HITECH, and other applicable regulations in your jurisdiction before deploying with real patient data.