Deploying
The CLI / server form is what you deploy — a single headless binary that runs a long-lived bridge in config mode. This page covers running it as a container and as a service, and the config/secrets patterns that suit each.
Docker
The CLI is published as a multi-arch image (amd64 + arm64):
docker run --rm --name mq-bridge -p 9091:9091 ghcr.io/marcomq/mq-bridge-app:latest
Mount the working directory at /app and seed the config on first run from one of the
templates baked into the image at /config:
touch input.log
docker run --rm --name mq-bridge -p 9091:9091 -v "$(pwd)":/app \
ghcr.io/marcomq/mq-bridge-app:latest --ui --init-config=/config/file-to-http.yml
- The default
latestimage is a plain multi-arch image foramd64andarm64. - IBM MQ support is published separately as the
latest-ibm-mq/ibm-mqtags,amd64only (no redistributable arm64 client yet). Start it with--platform=linux/amd64, or build yourself withcargo build --release --features=ibm-mq.
Ports in containers
On a host, the UI is never opened implicitly and
metrics bind loopback. In a container those defaults would be wrong for the opposite reason —
nothing is reachable until you publish it — so the image’s CMD is --ui, and metrics bind
0.0.0.0:9090 through ENV MQB__METRICS_ADDR rather than the command line: a Kubernetes pod
that sets args: replaces CMD wholesale, and the environment survives that. The container
boundary is the gate: without -p (or a Kubernetes Service), neither port leaves the container.
Docker replaces CMD wholesale as soon as you pass any argument of your own. That is what
keeps the headless modes headless:
The two settings are carried differently, and that difference is the whole design:
| Carried by | Survives an args: / command override? | |
|---|---|---|
Metrics on 0.0.0.0:9090 | ENV MQB__METRICS_ADDR | Yes |
| Web UI | CMD ["--ui"] | No |
Metrics live in ENV because a Kubernetes pod almost always sets args:, which replaces
CMD wholesale. Had the bind address ridden along in CMD, every such pod would silently
fall back to the host default of 127.0.0.1:9090 and go unscrapeable. As an environment
variable it survives any command-line override, and a pod that wants something else just
sets MQB__METRICS_ADDR (or metrics_addr in its ConfigMap).
The UI stays in CMD precisely because it is dropped on override — that is what keeps the
headless modes headless:
| Invocation | Result |
|---|---|
docker run image | Config mode, UI + metrics served |
docker run image copy … / mcp … | CMD dropped — headless, as those modes always are |
docker run image --config /app/x.yml | CMD dropped — add --ui if you want the UI |
Kubernetes with args: […] | No UI; metrics still served |
Kubernetes with no args | UI + metrics served, reachable only via a Service |
The second docker run example above passes --init-config, which is why it also passes
--ui.
To build the image from source, see
BUILD.md.
Configuration in containers / Kubernetes
Configuration is hierarchical — files plus environment variables — which is exactly what container and Kubernetes deployments want:
- Bake a base
config.ymlinto the image or mount it as a ConfigMap. - Override any field per environment with
MQB__{ROUTE}__{PATH}env vars (double underscores between segments), e.g.MQB__KAFKA_TO_NATS__INPUT__KAFKA__TOPIC=my-topic. - Reference secrets inline with
${ENV_VARIABLE_NAME:-default}; a.envfile in the working directory is auto-loaded for local development.
See Configuration grammar for the full env-var mapping and Secrets & interpolation for keeping credentials out of committed config.
Choosing the run shape
| You want… | Run it as… |
|---|---|
| A one-shot batch move (finite source), exit 0 on success | copy … --drain (see Quick start) |
| A long-lived bridge with one or more routes | config mode: mqb --config config.yml |
| The bridge driven by an LLM agent | mcp mode |
In config mode the CLI can also serve the browser UI on the configured port, but never
implicitly: it needs ui_addr in the config or an explicit --ui. An unattended start — a
service unit, a container, a script — is headless unless you asked for the UI, so production
deployments opt in rather than opt out. Where you do serve it, front it appropriately.
See Starting the web UI.
Security checklist for production
- TLS on every sensitive endpoint (
tls.required: true+ca_file, mTLS where supported). Never setaccept_invalid_certs: true. Pick the crypto provider feature (rustls-aws-lcfor FIPS-capable / post-quantum, orrustls-ring). - Keep payloads out of logs: run above
tracelevel (payloads log attrace). - Do not commit secrets: source them from a secrets manager or env vars.
- Consider the config security modes (plain, extracted secrets, encrypted config, encrypted history) based on the runtime target and available key storage.
Full hardening notes (including the PCI-DSS-oriented checklist) are in the TLS & security hardening section.
Observability
Wire up the metrics middleware and scrape the Prometheus endpoint; ship
the JSON logs to your aggregator. See Observability & metrics.
Continuous deployment of this book
The book is published to GitHub Pages by
.github/workflows/docs.yml
on every push to main that touches the root engine docs or
apps/mq-bridge-app/dev/docs/**. It runs the local
apps/mq-bridge-app/dev/docs/sync-engine-docs.sh before building the book. To
build it locally, run the same commands from the repository root. See
the book’s README.