Context
Multiple downstream projects (this repo's harness, k8s charts, CI fixtures, security-test rigs) consume the openemr/openemr Docker image and end up reimplementing pieces of the install/bootstrap dance. The current image is great when used exactly as the canonical docker-compose example does, but small departures (bind-mounting sites/, init containers, headless CI) hit the same set of sharp edges. This issue proposes a coherent set of small, backwards-compatible upstream changes so downstreams stop reinventing the bootstrap.
Related: #500 (permissions on bind-mounted sites/), #562 (-c misrouted in auto_configure invocation). Both touch the same code path.
Root cause that motivated this
sites/default/ is simultaneously the seed of install-time files and the runtime install target. Bind-mounting a host directory over sites/ (which downstreams do for persistence, inspection, or test isolation) shadows the seed the entrypoint expects to copy from. The container then can't write sqlconf.php, but it also doesn't detect "fresh install needed" — it loops on Plan to try an upgrade from 0 to 7 and emits PHP Fatal error: Failed opening required '...sqlconf.php' on every subsequent CLI invocation. The failure is silent-ish (no exit 1), so health probes time out without a clear signal.
This is one of several symptoms of the broader pattern: the install path is entangled with the Apache entrypoint and auto-detects state instead of being addressable as a tool.
Proposed changes (all additive, all backwards compatible)
A. Relocate the install seed out of the runtime path
Move the shipped sites/default/ seed to e.g. /usr/share/openemr/sites-skel/default/. Entrypoint copies skel → sites/ only when sites/default/sqlconf.php is absent. Existing volume layouts are untouched; new bind-mounts stop self-destructing. This single change fixes the root-cause symptom above.
B. A real headless-install CLI
Expose php bin/openemr-install --from-env (or equivalent) that is idempotent and explicit:
- Empty DB → install schema, write
sqlconf.php, exit 0
- Already installed → exit 0
- Partial / inconsistent state → exit non-zero with a diagnostic; never silently fall through to upgrade
Today auto_configure.php is entangled with the Apache entrypoint. Splitting it out gives downstream init containers and CI steps a real tool to call without booting Apache. The existing entrypoint behavior stays the default.
C. Detect the bind-mount-shadow case and fail loudly
When the entrypoint sees install env vars set, sites/default/ empty, and the image's skel populated, either (a) copy from skel, or (b) print a one-line diagnostic pointing at A's docs and exit non-zero. Pure error-quality improvement; can't break anyone.
D. Ship a proper container healthcheck binary
/usr/local/bin/openemr-healthy that checks FHIR metadata and confirms install completed. Compose healthcheck: calls it. Replaces the hand-rolled curl ... || exit 1 loops every downstream writes.
E. Publish a pre-installed image tag
openemr/openemr:<version>-prebaked (or similar) that boots ready-to-serve with a known test admin account. New tag, doesn't touch existing ones. Eliminates the install dance entirely for CI fixtures — boot time drops from minutes to seconds. This is the highest-leverage change for downstream CI, but also the largest upstream lift (CI step to bake + publish).
Suggested sequencing
- A + C together — small PHP/shell change in the entrypoint, fixes the most common failure mode, no new surface area.
- D — trivial, replaces a pattern every downstream already implements.
- B — formalizes the install contract every downstream is currently reimplementing. Bigger design conversation.
- E — separate proposal; deserves its own discussion of tag conventions and rebuild cadence.
Happy to drive PRs for (A), (C), (D) once there's directional agreement here. (B) and (E) want a design pass first.
Why this is worth doing
The fragility is invisible to anyone using the canonical compose file untouched, but it taxes every other consumer in proportion to how serious their use case is — CI fixtures, k8s charts, security scanners, and dev harnesses all pay the same setup tax. Small upstream changes here remove that tax for many downstreams at once.
Context
Multiple downstream projects (this repo's harness, k8s charts, CI fixtures, security-test rigs) consume the
openemr/openemrDocker image and end up reimplementing pieces of the install/bootstrap dance. The current image is great when used exactly as the canonical docker-compose example does, but small departures (bind-mountingsites/, init containers, headless CI) hit the same set of sharp edges. This issue proposes a coherent set of small, backwards-compatible upstream changes so downstreams stop reinventing the bootstrap.Related: #500 (permissions on bind-mounted
sites/), #562 (-cmisrouted inauto_configureinvocation). Both touch the same code path.Root cause that motivated this
sites/default/is simultaneously the seed of install-time files and the runtime install target. Bind-mounting a host directory oversites/(which downstreams do for persistence, inspection, or test isolation) shadows the seed the entrypoint expects to copy from. The container then can't writesqlconf.php, but it also doesn't detect "fresh install needed" — it loops onPlan to try an upgrade from 0 to 7and emitsPHP Fatal error: Failed opening required '...sqlconf.php'on every subsequent CLI invocation. The failure is silent-ish (noexit 1), so health probes time out without a clear signal.This is one of several symptoms of the broader pattern: the install path is entangled with the Apache entrypoint and auto-detects state instead of being addressable as a tool.
Proposed changes (all additive, all backwards compatible)
A. Relocate the install seed out of the runtime path
Move the shipped
sites/default/seed to e.g./usr/share/openemr/sites-skel/default/. Entrypoint copiesskel → sites/only whensites/default/sqlconf.phpis absent. Existing volume layouts are untouched; new bind-mounts stop self-destructing. This single change fixes the root-cause symptom above.B. A real headless-install CLI
Expose
php bin/openemr-install --from-env(or equivalent) that is idempotent and explicit:sqlconf.php, exit 0Today
auto_configure.phpis entangled with the Apache entrypoint. Splitting it out gives downstream init containers and CI steps a real tool to call without booting Apache. The existing entrypoint behavior stays the default.C. Detect the bind-mount-shadow case and fail loudly
When the entrypoint sees install env vars set,
sites/default/empty, and the image's skel populated, either (a) copy from skel, or (b) print a one-line diagnostic pointing at A's docs and exit non-zero. Pure error-quality improvement; can't break anyone.D. Ship a proper container healthcheck binary
/usr/local/bin/openemr-healthythat checks FHIRmetadataand confirms install completed. Composehealthcheck:calls it. Replaces the hand-rolledcurl ... || exit 1loops every downstream writes.E. Publish a pre-installed image tag
openemr/openemr:<version>-prebaked(or similar) that boots ready-to-serve with a known test admin account. New tag, doesn't touch existing ones. Eliminates the install dance entirely for CI fixtures — boot time drops from minutes to seconds. This is the highest-leverage change for downstream CI, but also the largest upstream lift (CI step to bake + publish).Suggested sequencing
Happy to drive PRs for (A), (C), (D) once there's directional agreement here. (B) and (E) want a design pass first.
Why this is worth doing
The fragility is invisible to anyone using the canonical compose file untouched, but it taxes every other consumer in proportion to how serious their use case is — CI fixtures, k8s charts, security scanners, and dev harnesses all pay the same setup tax. Small upstream changes here remove that tax for many downstreams at once.