Skip to content

Self Hosting

Ankit Upadhyay edited this page Aug 13, 2026 · 1 revision

Self-hosting

This page states honestly where self-host packaging stands, what exists on its branch, and what a deployer has to do themselves in the meantime. It is for anyone evaluating whether they can run openrunic today.

Current state

You cannot self-host openrunic yet. There are no published container images, no release artifacts, no compose file on dev, and no installation documentation in the repository. The project is pre-alpha and the README says plainly not to run it in production.

Packaging work is under way on the feat/ops-selfhost branch as uncommitted working-tree state. What exists there:

File What it is
apps/api/Dockerfile A multi-stage image for the API with two published targets
apps/api/docker-entrypoint-migrate.sh A boot-time schema step
apps/api/docker-migrate-helper.mjs The helper the entrypoint calls
.dockerignore Build-context exclusions

None of it is merged, none of it is tested in CI, and there is no web image or compose file yet.

The design being built

Worth knowing because it constrains contributions elsewhere.

One command on a stock Linux box. The target is docker compose up, from install to first appointment in under thirty minutes, on whatever Docker the distribution shipped. This is why ADR-0004 refuses a machine-learning runtime in the core deployment: a Python and native-tensor runtime would put the install-time bar out of reach.

No build-kit-only syntax. The Dockerfile deliberately uses no mount, link, or heredoc syntax. A self-hoster runs whatever Docker their distribution shipped, and a build that needs a newer builder is a support ticket on day one.

The build context is the repository root, not the app directory, because the API is a workspace in a pnpm monorepo and needs the root lockfile plus two workspace packages:

docker build -f apps/api/Dockerfile -t openrunic-api .

Two targets from one file. A runtime target carrying the pruned production server, and a migrate target carrying the Prisma CLI and the migration history. The migrate container runs once at boot and exits.

The pinned toolchain travels with the image. Corepack activates the pnpm version from the root packageManager field, so an image can never build with a different pnpm than the lockfile was written by. Husky is disabled during the image build, because there is no git directory in the build context.

The boot sequence is idempotent by construction. The migrate entrypoint does three things in order: wait until the database answers a real query, apply every pending migration, and seed the demo practice only into an empty database. docker compose up is something an operator runs many times against the same volume, and it must not fail the second time.

Its failure messages are written for an operator rather than a developer. A missing database URL says which file to copy and which command to run, not which variable is undefined.

What a deployer has to provide

Whenever packaging does ship, these remain the deployer's responsibility, and docs/compliance.md states them as expectations rather than assumptions.

TLS in transit. The API terminates no TLS of its own. Put it behind a reverse proxy.

Encryption at rest. Postgres-level or disk-level. Not something the application arranges.

Object storage. Binary content never lives in Postgres. Document, DiagnosticReport, Remittance, and Statement all carry storage keys rather than bytes, so a deployment needs somewhere to put them and a policy for who can read them.

Authentication. There is none in the repository today. See Security model.

Terminology content. openrunic ships no code content, because the major systems carry their own licences. TerminologyCode is a bring-your-own cache and each deployment loads only what it is licensed for.

Backups. See Backup and restore.

Everything regulatory. openrunic is not a certified device and is not compliant by itself. See Compliance posture.

Things a deployment will need to reconsider

Reading the code as an operator rather than a contributor surfaces several gaps worth naming.

  • No CORS headers are set. A browser on another origin cannot call the API.
  • No rate limiting, request timeout, or body-size limit exists in the API.
  • Row-level security is designed but not enabled, so tenant isolation currently rests on the application layer plus its tests.
  • The CI migration job pins Postgres 17. Once a production database exists, that pin should match its major exactly. Migration SQL proven against a newer server than production is not proof.

Related pages

Clone this wiki locally