Configuration sources
How the gateway assembles its config from files, remote URLs, and inline base64 — the MCPG_CONFIG env var and the repeatable --config flag, how layers merge, how secrets resolve, and what hot-reloads.
The gateway builds its running configuration by layering sources — each a YAML document — in a defined order, then overlaying environment variables. This page is the complete model: where config comes from, how layers merge, how secrets resolve, and what changes on a hot-reload. For the field-by-field config surface, see the configuration reference.
The two inputs
Config comes from two ordered inputs. Both are later-wins overlays, and the second is applied after the first:
MCPG_CONFIG— an environment variable holding a single file path, or a path-separator-joined list of files (:on Unix,;on Windows).--config <source>— a repeatable CLI flag. Each value is one source, applied after theMCPG_CONFIGfiles. Because a--configvalue is not path-split, it can be more than a file: a URL or inline content.
# Env files only
MCPG_CONFIG=base.yaml:prod.yaml mcpg
# Flag only
mcpg --config base.yaml --config prod.yaml
# Both: env files are the base, --config layers on top
MCPG_CONFIG=base.yaml mcpg --config prod.yaml
Running with no config at all is valid — the gateway boots on built-in defaults
plus any MCPG_* environment overrides.
Source forms
Every MCPG_CONFIG entry is a file. A --config source is classified by its
scheme:
| Source | Example | Resolved as |
|---|---|---|
| Local path | gw.yaml, /etc/mcpg/gw.yaml | a file, read at load time |
file:// path | file:///etc/mcpg/gw.yaml | a file |
https:// URL | https://cfg.example.com/gw.yaml | fetched at boot over TLS |
http:// URL | http://cfg.internal/gw.yaml | refused unless opted in (below) |
base64: | base64:Z2F0ZXdheToge30K | inline base64-decoded YAML |
data: URI | data:application/yaml;base64,Z2F0… | inline (RFC 2397, base64 form only) |
Files are re-read from disk on every hot-reload. Remote and inline
sources (https / base64 / data) are resolved once at boot, held in
memory — never written to disk — and reused as-is on reload.
Merge order
Layers merge deterministically, later winning:
built-in defaults
→ each MCPG_CONFIG file, in order
→ each --config source, in order
→ MCPG_* environment variables (always last, override everything)
- Objects deep-merge — a later layer that sets
gateway.server.request_timeout_mskeeps every other field the base layer defined undergateway.server. - Scalars and arrays replace wholesale — a later layer's list replaces the earlier list; it does not append.
This is what makes a base.yaml + prod.yaml overlay ergonomic: the override
only names the handful of fields it changes.
Environment-variable overrides
Any MCPG_* variable (double-underscore __ as the nesting separator) overrides
the matching config path, and it wins over every file/URL/inline layer:
# Override gateway.server.bind_address without touching the file
MCPG_CONFIG=gw.yaml MCPG_GATEWAY__SERVER__BIND_ADDRESS=0.0.0.0:8080 mcpg
The env var name mirrors the YAML path:
gateway.server.bind_address → MCPG_GATEWAY__SERVER__BIND_ADDRESS.
Secrets never live in a source
A config source — file, URL, or base64 — should never contain a literal secret. Reference secrets instead, and they resolve at runtime, after the config is loaded and hashed:
${env.VAR}— pulled from the process environment at startup.cred://<plugin_id>/<provider>— resolved by a credential-issuer plugin.
mcp:
capabilities:
tools:
- backend:
kind: http
headers:
Authorization: "Bearer ${env.UPSTREAM_TOKEN}"
Because references (not values) are what live in the source, the config digest in
the mcpg.config.loaded audit event stays reproducible from the source alone and
never leaks a secret. This holds identically whether the layer came from a file,
an https:// URL, or a base64: blob.
What hot-reloads
The gateway re-reads config without a restart on three triggers: SIGHUP, the
POST /admin/v1/config:reload admin endpoint, and — when enabled — the
gateway.config_watch file poller. On reload:
- File layers are re-read from disk, so edits to a file take effect.
- Remote and inline layers reuse their boot snapshot — a reload does not re-fetch a URL or re-decode a blob. To pick up a changed remote config, restart the gateway.
The file poller only watches the file layers; a gateway whose config is entirely remote/inline has nothing on disk to watch.
Security posture for remote config
Fetching config over the network at boot is powerful, so it's fenced:
- HTTPS only. A plaintext
http://source is refused — a network attacker who can rewrite your config owns your gateway. Override only for a trusted network (e.g. a link-local metadata service) withMCPG_CONFIG_ALLOW_INSECURE_HTTP=1. - Bounded. A remote fetch has a 10-second timeout and a 5 MiB cap; a non-2xx response fails the boot.
- In memory. Remote and inline config is never written to disk, so a secret-bearing config body doesn't linger in a temp file.
Worked examples
Base + production overlay (files):
MCPG_CONFIG=base.yaml:production.yaml mcpg
# production.yaml only needs the fields it changes
Remote base, local override (GitOps / config service):
mcpg --config https://config.internal.example.com/mcpg/base.yaml \
--config /etc/mcpg/node-override.yaml
# fleet-wide base served centrally; per-node file wins on top
Inline config in a constrained environment (CI, a container arg, a serverless slot):
# No file to mount — ship the whole config as one flag value
mcpg --config "base64:$(base64 -w0 gw.yaml)"
Env-only, no files:
MCPG_GATEWAY__SERVER__BIND_ADDRESS=0.0.0.0:8787 \
MCPG_OBSERVABILITY__LOGS__LEVEL=info \
mcpg
Troubleshooting
| Symptom | Cause |
|---|---|
config file not found: … | a file layer path is wrong or unmounted |
refusing to fetch config over plaintext http:// | use https://, or set MCPG_CONFIG_ALLOW_INSECURE_HTTP=1 for a trusted network |
--config base64: payload is not valid standard base64 | the blob isn't standard base64 (check base64 -w0, no line wraps) |
--config data: URI must be base64-encoded | only the data:...;base64,<payload> form is accepted |
fetch config from …: HTTP 404 | the remote config URL is wrong or unauthorized |
| A remote config change didn't take effect on reload | remote/inline layers are boot snapshots — restart to re-fetch |
| An unknown-field error at boot | a typo or stale key; deny_unknown_fields is on at every level. Run mcpg config check. |
Validate any source set before shipping with mcpg config check.
See also
- Configuration reference — every config field and type.
mcpgCLI reference — the--configflag andMCPG_CONFIG.- Reverse tunnels — a gateway can even boot from a remote config and serve with no public port.