The local stack runs with Docker Compose from the root of this repository. compose.yaml only includes the files under compose/, one per layer, and every service belongs to a profile.
-
Docker Engine or Docker Desktop with Compose 2.20 or newer (
include:support). -
GNU Make.
-
Terraform 1.6 or newer, to provision Keycloak.
-
For running services from source: Node.js and the
rmu-*checkouts next to this repository.
cp .env.example .env # local-only credentials and defaults
make up # starts the profiles in COMPOSE_PROFILES
make keycloak-init # once Keycloak answers on http://localhost:8090/ssoBefore the Terraform step, create terraform/stacks/keycloak-local/terraform.tfvars from its .example; see operations/keycloak-terraform.adoc. The APIs start before the realm exists and fail token validation until it has been applied.
| Profile | File | Services |
|---|---|---|
|
|
MongoDB, PostgreSQL, Keycloak, Kafka, Mailpit, the local S3 ( |
|
|
Every backend API, from the published |
|
|
Micro-frontends as static images (shell, core, strategic, tactical, npcs, items, spells, wiki, assets), served through the gateway; see architecture/frontends.adoc. |
|
|
Kafka UI. |
|
|
Caddy on port 80: the shell at |
|
|
Grafana, Loki, Tempo, Prometheus and an OTel collector ( |
COMPOSE_PROFILES in .env selects what docker compose up -d starts. The default is infrastructure,apis,mfes,gateway,tools. On the command line, --profile replaces it for that run.
|
Important
|
apis and tools depend on services of infrastructure. Without it, Compose fails with depends on undefined service. The make up-* targets add it for you.
|
| Goal | Command |
|---|---|
Everything in |
|
Only the infrastructure, to run APIs from source |
|
Infrastructure and APIs |
|
Infrastructure and tools |
|
Add the gateway or observability to what is running |
|
Validate the files and list the active services |
|
make down, make ps and make logs [s=<service>] act on every profile, so nothing is left running by accident. make down keeps the volumes; docker compose --profile '*' down -v also deletes the local data.
| Service | Address |
|---|---|
Keycloak |
http://localhost/sso (gateway), http://localhost:8090/sso (admin console) |
Mailpit (realm mail) |
|
MongoDB |
|
Kafka from the host |
|
Kafka UI |
|
Local S3 |
http://localhost:8333 ( |
Gateway |
http://localhost ( |
Grafana |
http://localhost:3000 (admin/admin) |
OTLP |
|
APIs |
|
Each image is labcabrera/rmu-api-<name> or labcabrera/rmu-mfe-<name> with the tag ${RMU_<KIND>_<NAME>_VERSION}, latest by default (local for the items, spells and wiki MFEs, which have no published image yet). latest is fine for day-to-day work; set explicit versions in .env to reproduce a release or a bug.
To run your checkouts as containers:
make up-local # build :local images of every checkout and start the stack with them
make up-local p="rmu-api-tactical rmu-mfe-tactical" # only some projects
make build-local p=rmu-mfe-core NO_CACHE=1 # only buildup-local also runs every project that already has a :local image; a RMU_*_VERSION set in the shell wins. TAG= changes the tag, and BASE_DIR (or RMU_WORKSPACE_DIR) the folder holding the checkouts.
-
Start everything else, then stop the container of the API you are working on, for example
docker compose stop rmu-api-core. -
In the API repository, copy
.env.exampleto.env. From the host, uselocalhostaddresses: Mongolocalhost:27017, Keycloakhttp://localhost:8090/ssofor the JWKS and token URIs, the issuerhttp://localhost/sso/realms/rmu(RMU_IAM_ISSUER_URI) and the audiencermu-api(RMU_IAM_AUDIENCE), Kafkalocalhost:9093. -
pnpm start:dev(Node 24,corepack enable).
Other containers still call http://rmu-api-core:3001, which no longer resolves. To exercise those calls, run the API as a container built from your checkout and attached to the rmu-network network. The APIs with releases do this with make docker-build docker-run (it needs a .env.docker), or use make up-local p=rmu-api-<name> here.
make mfes-dev # MFEs next to this repository
make mfes-dev BASE_DIR=~/src/rmu # elsewhereIt opens a gnome-terminal tab per checked-out MFE running pnpm run start:live, plus rmu-mfe-assets. Without gnome-terminal, it asks you to start each one by hand. Stop the MFE containers (or leave mfes out of the profiles) and open http://localhost: the gateway falls back to the dev servers; see [_working_on_one_mfe].
The gateway profile runs Caddy on port 80 (RMU_GATEWAY_PORT). It is the single origin of the application, http://localhost. Routes are in compose/config/caddy/Caddyfile:
| Path | Upstream |
|---|---|
|
|
|
The remote’s container, or its dev server on the host ( |
|
|
|
Core, Strategic, Tactical APIs |
|
Items, NPCs, NPC Names, Spells APIs |
|
Attack Tables API |
|
Users, Media APIs |
|
Keycloak |
The prefix is stripped, so http://localhost/api/core/v1/races reaches http://rmu-api-core:3001/v1/races. The APIs live under /api/ so that /core/…, /strategic/… stay free for the shell’s routes. Upstreams are resolved per request: the gateway starts with any subset of services, and a stopped one answers 502. Any other /mfe/<name>/ answers 404 until its MFE is added.
Everything runs from containers except the MFE you are working on, which runs from source with the debugger attached. The gateway switches to the dev server on its own when the container is stopped, so nothing else is restarted or reconfigured.
make up # with mfes and gateway in COMPOSE_PROFILES
make mfe-dev s=core # docker compose stop rmu-mfe-core
# VS Code in rmu-mfe-core: F5 "Debug core in the shell (gateway)"
# runs `pnpm run start:live` (:8089) and opens Chrome at http://localhost/core
make mfe-container s=core # back to the image-
Always open http://localhost, not the dev server port: the shell’s configuration (
env-config.js) uses gateway paths such as/mfe/core/and/api/core/v1. -
Inside the shell, a remote reads the shell’s configuration, so the remote’s own
.envonly matters when it is opened standalone. -
HMR connects straight to the dev server (
ws://localhost:<port>/ws), not through the gateway. -
If a remote is not available at all (container stopped and no dev server), only its route shows "Module not available".
To try a local build of an MFE, make docker-build in its repository tags labcabrera/rmu-mfe-<name>:local; select it with RMU_MFE_<NAME>_VERSION=local in .env, or use make up-local p=rmu-mfe-<name>. The same works for s=shell, s=strategic, s=tactical, s=npcs, s=items, s=spells and s=assets.
The observability profile runs grafana/otel-lgtm with the Loki, Prometheus, Tempo and Pyroscope datasources already provisioned. Grafana is at http://localhost:3000 (admin/admin).
APIs are not instrumented yet. Once an API supports OpenTelemetry, configure it with the standard variables:
OTEL_EXPORTER_OTLP_ENDPOINT: http://rmu-otel:4317
OTEL_SERVICE_NAME: rmu-api-<name>-
A stopped MFE does not fall back to its dev server: the gateway reaches the host through
host.docker.internal(host-gateway). Check that the dev server listens on0.0.0.0and that a host firewall (ufw) allows traffic from the Docker bridge to its port. -
rmu-networkalready exists: the network was created by hand withdocker network create. Rundocker compose --profile '*' down && docker network rm rmu-networkonce and start again. -
Kafka from the host bootstraps but then fails: use
localhost:9093. Port 9092 advertisesrmu-kafka-broker:9092, which only resolves inside the Compose network. -
MongoDB does not start on Linux 6.19+: MongoDB 8.0 is affected by SERVER-121912. The service sets
GLIBC_TUNABLES=glibc.pthread.rseq=1to avoid it. -
APIs reject every token: the realm has not been provisioned,
RMU_IAM_CLIENT_SECRETin.envdoes not matchrmu_client_secretinterraform.tfvars, or the API’sRMU_IAM_ISSUER_URI/RMU_IAM_AUDIENCEdo not match the token (issmust behttp://localhost/sso/realms/rmu,audmust containrmu-api). -
Login asks for credentials in every new tab: open the shell through the gateway (http://localhost), so the Keycloak cookie is first-party, and check "Remember me" to keep the session after closing the browser.
|
Warning
|
Some initialization data is intentionally not in this public repository, because it may contain copyrighted Rolemaster Unified material owned by Iron Crown Enterprises. A fresh environment starts, but lacks part of the reference content. Restore it from a backup of your own (operations/mongo-backup-restore.adoc) and never commit it. |