---
title: "Deploy OpenEMR"
description: "Electronic health records and practice management for clinics"
category: "Other"
url: https://railway.com/deploy/open-emr
---

# Deploy OpenEMR

Electronic health records and practice management for clinics

**[Deploy OpenEMR on Railway](https://railway.com/template/open-emr)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/open-emr/manifest.json

- **Creator:** A3A
- **Category:** Other
- **Total deploys:** 1

## Template content

### MariaDB https://cdn.jsdelivr.net/gh/gridalpha/openemr-railway@9fc62adf4ca65bec49785e5a7884374eec1ce2d6/icons/mariadb-light.svg

- **Image:** mariadb:12.3
- **Start command:** `/bin/sh -c 'M=$(cat /sys/fs/cgroup/memory.max 2>/dev/null || echo max); case "$M" in max|"") M=2147483648;; esac; P=$((M/1024/1024/4)); if [ "$P" -lt 128 ]; then P=128; fi; echo "[railway] innodb-buffer-pool-size=${P}M (memory.max=$M)"; exec docker-entrypoint.sh mariadbd --character-set-server=utf8mb4 --datadir=/var/lib/mysql/data --innodb-buffer-pool-size=${P}M'`

### Redis https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/redis.svg

- **Image:** redis:8.2
- **Start command:** `/bin/sh -c "rm -rf $RAILWAY_VOLUME_MOUNT_PATH/lost+found/ && exec docker-entrypoint.sh redis-server --requirepass $REDIS_PASSWORD --save 60 1 --dir $RAILWAY_VOLUME_MOUNT_PATH"`

### OpenEMR https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/openemr-light.svg

- **Source:** https://github.com/gridalpha/openemr-railway
- **Health check:** /meta/health/readyz
- **Public domain:** Yes

## Documentation

# Deploy and Host OpenEMR on Railway

OpenEMR is the most widely deployed open-source electronic health record and practice management system in the world, used by clinics and community health centres in over a hundred countries. It covers the whole clinical day: appointment calendar, patient charts, encounter notes, prescriptions, lab results, insurance and claims, and reporting on top. It is ONC-certified, ships a FHIR R4 and OAuth2 API, and speaks the formats other health systems expect (C-CDA, HL7, X12), so a practice can run it as its only clinical system.

Deploy OpenEMR on Railway and you get three services wired together: **OpenEMR**, running Apache and PHP 8.5 behind a public HTTPS domain; **MariaDB**, holding every patient record on its own volume; and **Redis**, holding PHP sessions so clinicians stay signed in across restarts. Browser traffic reaches OpenEMR over the public domain while the database and cache stay on the private network with no public address. The installer runs on first boot, creates the schema and the first administrator account, and every later boot compares the code's schema revision against the database and migrates it when a new release has landed.

![Diagram of the OpenEMR, MariaDB and Redis services on Railway](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789487835/openemr-architecture.webp)

## Getting Started with OpenEMR on Railway

Set `OE_USER` and `OE_PASS` before deploying — they become the first administrator account and are the only two values you need to supply. The first boot takes a few minutes while the installer builds the clinical schema. When the URL answers you land on the sign-in page; sign in with the credentials you chose.

You arrive at the tab shell: Calendar, Finder, Flow, Recalls, Messages, Patient, Fees, Modules, Procedures, Admin, Reports. The quickest way to confirm the deployment is healthy is to create a patient — **Patient → New/Search**, fill in a name, date of birth and birth sex, and save. The record appears in **Finder**, and clicking the name opens the Medical Record Dashboard with cards for Allergies, Medications, Prescriptions, Care Team, Demographics, Billing and Appointments. If that chart renders, the app, the database and the session store all work.

Next, open **Administration → Globals** for your practice name, time zone and locale, **Administration → Facilities** to create your clinic, and **Administration → Users** to add clinicians. Record deletion is gated in Globals → Security as well as by access level, so a fresh installation answers "Access denied" until you enable it.

![OpenEMR appointment calendar for a single provider](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789487837/openemr-clinic-dashboard.webp)

![OpenEMR patient finder listing two patient records](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789487839/openemr-patient-finder.webp)

![Medical record dashboard for a patient in OpenEMR](https://res.cloudinary.com/rroe4rtk/image/upload/f_auto,q_auto/v1789487841/openemr-patient-chart.webp)

## About Hosting OpenEMR

OpenEMR is a PHP application backed by MySQL or MariaDB. Self-hosting means you own the patient data outright — no per-seat licence, no vendor deciding what you can export. Practices choose it when a commercial EHR's per-provider pricing does not fit, when they need to customise forms and workflows, or when data residency rules out a hosted service.

- Appointment calendar with per-provider views, recurring slots and recall lists
- Demographics, insurance and a full encounter-based clinical chart
- Prescriptions with interaction checking, plus lab and imaging results
- Billing: fee sheets, superbills, X12 837 claims and ERA posting
- Patient portal, FHIR R4 and OAuth2 API, C-CDA import/export, HL7 interfaces
- Immunisation registries, decision rules, reports, 30+ languages

Each concern gets its own service. **OpenEMR** serves every browser request and holds a volume at its `sites/` directory, where the site configuration and all uploaded documents live — scanned referrals, consent forms, imported reports. **MariaDB** holds the relational data on its own volume, with its InnoDB buffer pool sized from the container's memory limit rather than the 128 MB default. **Redis** keeps PHP sessions outside the container.

## Why Deploy OpenEMR on Railway

A clinical system has to stay up and stay private; Railway handles what usually needs a sysadmin.

- Managed TLS on a public domain; database and cache stay private
- Persistent volumes for patient documents and the database
- Health checks and automatic restarts on a crashed container
- Deploys from a public GitHub repo you can fork and change

## Common Use Cases

- A single-site clinic replacing a per-provider commercial EHR with one it owns
- A community health centre needing multi-facility records and registry reporting
- A developer building against the FHIR R4 API with no local infrastructure

## Dependencies for OpenEMR

- **OpenEMR** — built from [gridalpha/openemr-railway](https://github.com/gridalpha/openemr-railway), one layer on the official [`openemr/openemr`](https://hub.docker.com/r/openemr/openemr) image
- **MariaDB** — `mariadb:12.3`, what OpenEMR's production compose file and test suite run against
- **Redis** — `redis:8.2`, the PHP session store

### Environment Variables Reference

| Variable | Purpose |
|---|---|
| `OE_USER` / `OE_PASS` | First administrator, created during install |
| `MYSQL_USER` / `MYSQL_PASS` | Scoped database account the app runs as |
| `REDIS_SERVER` / `REDIS_PORT` | Session store address; `REDIS_SERVER` is a bare host |
| `OPENEMR_SETTING_site_addr_oath` | Base URL used to build OAuth2 and FHIR endpoints |
| `OPENEMR_SETTING_` | Writes any global each boot; overrides the admin UI |

### Deployment Dependencies

- Source:  · Image: 
- Docs: 
- Runtime: PHP 8.5 on Apache (Alpine), MariaDB 12.3, Redis 8.2

## Hardware Requirements for Self-Hosting OpenEMR

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 2–4 vCPU |
| RAM | 2 GB | 4–8 GB |
| Storage | 5 GB database + 5 GB documents | 20 GB+, grows with scans |
| Runtime | PHP 8.2+, MariaDB 10.6+ | PHP 8.5, MariaDB 12.x |

Apache runs in prefork mode and every worker is a full PHP interpreter, so memory is the limit that bites first. This template derives its worker ceiling from the container's memory quota at boot, so a traffic spike does not become an out-of-memory restart.

## Self-Hosting OpenEMR

The official image is configured entirely through environment variables. The smallest working stack is the database plus the app:

```
docker network create oe
docker run -d --name oe-db --network oe -v oe-db:/var/lib/mysql \
  -e MARIADB_ROOT_PASSWORD=change-me mariadb:12.3
docker run -d --name oe --network oe -p 8080:80 \
  -e MYSQL_HOST=oe-db -e MYSQL_ROOT_PASS=change-me \
  -e MYSQL_USER=openemr -e MYSQL_PASS=change-me-too \
  -e OE_USER=admin -e OE_PASS=change-me-three \
  -v oe-sites:/var/www/localhost/htdocs/openemr/sites \
  openemr/openemr:latest
```

To build the image this template uses, which adds reverse-proxy handling:

```
git clone https://github.com/gridalpha/openemr-railway
cd openemr-railway &amp;&amp; docker build -t openemr-railway .
```

Either way, mount a volume at `sites/`: it holds the site configuration and every uploaded document, and losing it loses those files even if the database survives.

## Is OpenEMR Free?

OpenEMR is free and open source under GPL-3.0: no licence fee, no per-provider charge, no feature tier held back. A commercial support ecosystem exists — vendors sell hosting, training and implementation — but nothing in the software requires it. On Railway you pay only for the compute, memory and volume storage the three services use, which for a small practice is a modest monthly bill rather than per-seat pricing.

## FAQ

**What is OpenEMR?**
An open-source electronic health record and practice management system. It handles scheduling, patient charts, prescriptions, lab results, billing and reporting for a clinic, and is ONC-certified for use in the United States.

**What does this Railway template deploy?**
Three services: OpenEMR on a public HTTPS domain with a volume for site files and documents, MariaDB on a private volume for the clinical database, and Redis for PHP sessions. The installer runs on first boot and creates your administrator account.

**Why does the template include MariaDB rather than a managed MySQL database?**
OpenEMR's own production compose file and continuous integration run against MariaDB, and its startup scripts use the MariaDB client tools. Running what upstream tests against avoids version and TLS mismatches that only surface partway through installation.

**Why is Redis included if OpenEMR can store sessions in files?**
File sessions live on the application's volume and are lost whenever the container is recreated, signing every clinician out on each deploy. Redis keeps them outside the container, so a redeploy is invisible to whoever is using the app.

**How do I upgrade self-hosted OpenEMR to a new release?**
Redeploy. The image compares its schema revision against the one in your database on every boot and runs the upgrade scripts when they differ, so a release migrates itself.

**Does OpenEMR support the FHIR API and patient portal?**
Yes, both ship with the application. The FHIR R4 and OAuth2 endpoints are built from the base URL in `OPENEMR_SETTING_site_addr_oath`, which this template points at your public domain, and the patient portal is enabled from Administration → Globals → Portal.


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/open-emr
