Skip to main content

Server settings

The gateway is configured entirely through environment variables (operational settings) and the config file (routing, plugins, tracing). This page is the reference for the environment variables read at startup.

Security-sensitive settings

A few variables relax authentication or expose internals and must never be enabled on a network-reachable deployment. They are marked (security-sensitive) below: MASTER_KEY, ALLOW_UNAUTHENTICATED_PROXY, and ENABLE_PPROF. Set GATEWAY_ENV=production (see Production mode) so the two most dangerous misconfigurations โ€” an unauthenticated proxy and a * CORS origin โ€” refuse to start instead of shipping silently.

Quick referenceโ€‹

VariableDefaultPurpose
GATEWAY_CONFIG(none)Path to the JSON or YAML config file (format auto-detected)
PORT8080HTTP listen port
GATEWAY_ENV(none)Set to production to turn on startup safety checks โ€” see Production mode
MASTER_KEY(none)(security-sensitive) Bootstrap/break-glass admin + proxy credential; generate with ferrogw init
ALLOW_UNAUTHENTICATED_PROXYfalse(security-sensitive) Disables /v1 auth โ€” local dev only
CORS_ORIGINS(none)Comma-separated allowed origins, matched literally (no wildcard)
TRUSTED_PROXIESloopbackComma-separated CIDRs whose X-Forwarded-For/X-Real-IP are honored for client-IP resolution
RATE_LIMIT_RPS20Per-IP requests/second; the middleware is on by default โ€” 0 disables it
RATE_LIMIT_BURST40Per-IP burst capacity
ENABLE_PPROFfalse(security-sensitive) Exposes /debug/pprof profiling endpoints (admin scope required)
FERRO_MODEL_CATALOG_URLGitHub releasesOverride the model-catalog source (air-gapped / custom pricing)
FERRO_MODEL_CATALOG_TIMEOUT10sBounds the startup catalog fetch; 0 skips the remote fetch entirely
FERRO_MODEL_DISCOVERY_INTERVAL(off)Opt-in live /models refresh interval (Go duration, e.g. 6h)
FERRO_OLLAMA_MODELS(none)Comma-separated Ollama model list narrowing /v1/models
LOG_LEVELinfodebug, info, warn, error
LOG_FORMATjsonjson or text (human-readable for dev)

Core runtimeโ€‹

  • GATEWAY_CONFIG - path to the JSON or YAML config file (format auto-detected). When unset the gateway boots from environment-derived defaults (a fallback strategy over every provider whose key is present).
  • PORT - HTTP listen port (default 8080).
  • LOG_LEVEL - logging level (debug, info, warn, error; default info).
  • LOG_FORMAT - log output format; set LOG_FORMAT=text for human-readable development logs.

Credentials and authโ€‹

  • MASTER_KEY - (security-sensitive) the bootstrap and break-glass admin credential the gateway accepts for both admin (/admin/*) and proxy (/v1/*) requests. Generate one with ferrogw init. It has no key-store row, so unlike a stored key it cannot be revoked or expired without restarting the process โ€” treat it like a root password. If unset, the gateway starts but logs a warning and admin routes have no valid credential.
  • ALLOW_UNAUTHENTICATED_PROXY - (security-sensitive) when set to true, the /v1/* proxy and inference routes skip authentication entirely. This is a development-only convenience; the gateway logs a warning at startup when it is set, and GATEWAY_ENV=production refuses to start with it enabled.

ADMIN_BOOTSTRAP_KEY, ADMIN_BOOTSTRAP_READ_ONLY_KEY, and ADMIN_BOOTSTRAP_ENABLED were removed in v1.4.0 ; MASTER_KEY is the only bootstrap credential now.

Per-operator keys

Give each operator their own admin-scoped key via POST /admin/keys (or the dashboard) instead of sharing MASTER_KEY day to day โ€” a shared key can't be revoked for one person without rotating it for everyone, and credential-change records all name the same key. Reserve MASTER_KEY for bootstrap and break-glass recovery. See Auth.

Production modeโ€‹

GATEWAY_ENV=production (case-insensitive) turns on a fixed set of startup checks, reported together so fixing a deployment is one edit rather than one restart per problem:

SettingOutcome under GATEWAY_ENV=production
ALLOW_UNAUTHENTICATED_PROXY=trueRefused โ€” startup exits; every /v1/* data-plane endpoint would be unauthenticated
CORS_ORIGINS containing *Refused โ€” matched literally, so it denies every cross-origin request while reading as an allow-all
RATE_LIMIT_RPS=0Warned โ€” startup continues; a normal choice when limits are enforced by an ingress or upstream API gateway
ENABLE_PPROF=trueWarned โ€” profiling routes stay behind the admin scope; the risk is leaving them mounted past an incident
In-memory API_KEY_STORE_BACKENDWarned โ€” operator keys, dashboard sessions, and the audit trail are lost on restart, leaving MASTER_KEY as the only way back in

Outside production (GATEWAY_ENV unset or any other value), all five settings are honored with only a startup warning โ€” none of them is refused.

CORS and trusted proxiesโ€‹

  • CORS_ORIGINS - comma-separated list of allowed origins, matched literally against the request's Origin header โ€” there is no wildcard, so a CORS_ORIGINS=* entry allows nothing a browser would ever send (quotes are stripped before the check, so CORS_ORIGINS="*" is caught too). List each origin explicitly. Unset serves no CORS headers. An unlisted origin is denied by the absence of Access-Control-Allow-Origin, not by a refused preflight.
  • TRUSTED_PROXIES - comma-separated CIDRs of trusted reverse proxies; X-Forwarded-For/X-Real-IP is honored only from these (default: loopback, 127.0.0.0/8 and ::1/128). An invalid value exits at startup. This matters for the per-IP rate limiter below โ€” without the real proxy's CIDR listed, every request behind an untrusted proxy resolves to the same client IP and shares one bucket.

Rate limitingโ€‹

Per-IP rate limiting is enabled by default โ€” it is not opt-in.

  • RATE_LIMIT_RPS - per-IP requests per second (default 20). Set to 0 to disable the middleware entirely; an invalid or negative value is ignored with a startup warning and the default is used instead. Setting RATE_LIMIT_RPS alone also resets the burst back to the default 40 โ€” pair it with RATE_LIMIT_BURST for a custom rate/burst combination.
  • RATE_LIMIT_BURST - per-IP burst capacity (default 40). The limiter tracks at most 100,000 IPs.

Rejected requests are surfaced on the gateway_rate_limit_rejections_total metric โ€” see Monitoring.

Not the same knob as the rate-limit plugin

RATE_LIMIT_RPS=0 disables this per-IP middleware. The rate-limit plugin's requests_per_second: 0 means the opposite โ€” a rate the gateway cannot serve โ€” and is rejected at config load; disable that plugin with enabled: false instead. See Rate limiting.

Model catalogโ€‹

  • FERRO_MODEL_CATALOG_URL - override the model-catalog source URL. The catalog supplies pricing, capabilities, and lifecycle metadata used for cost estimation and /v1/models enrichment. By default the gateway fetches the catalog from the public GitHub releases of ferro-labs/model-catalog, with an embedded catalog as fallback, so the gateway never fails to start if the source is unreachable. Point this at an internal mirror for air-gapped deployments, or at a custom catalog for enterprise/custom pricing. Any userinfo and query parameters in the URL are stripped from logs, so tokens embedded in the URL are not leaked.
  • FERRO_MODEL_CATALOG_TIMEOUT - Go duration bounding the startup catalog fetch (default 10s). The fetch runs before the listener binds, so a blocked-egress deployment waits this long before falling back to the embedded catalog. Set 0 to skip the remote fetch entirely โ€” the way to keep a fully air-gapped gateway from spending any startup time on it.
  • FERRO_MODEL_DISCOVERY_INTERVAL - opt-in Go duration (e.g. 6h) to live-refresh model lists from provider /models endpoints after startup. Unset, unparseable, or under 1m leaves discovery disabled.

Each load attempt is recorded on the gateway_catalog_loads_total metric (source, result labels).

Ollama model listโ€‹

  • FERRO_OLLAMA_MODELS - comma-separated Ollama model list narrowing what /v1/models advertises. Neither variable is required โ€” Ollama serves whatever the operator pulled onto it, so an unset list lets the provider serve any model.
  • OLLAMA_MODELS - deprecated, read for one more release with a startup warning. This is Ollama's own variable for the models directory, not a model list; a path-shaped value (/โ€ฆ, ~โ€ฆ, ./โ€ฆ) with no comma is dropped with a warning rather than registered as a bogus model id. FERRO_OLLAMA_MODELS wins when both are set.

Profilingโ€‹

  • ENABLE_PPROF - (security-sensitive) when set to true, mounts the Go net/http/pprof handlers under /debug/pprof (heap, goroutine, profile, trace, etc.), alongside /debug/vars (expvar). Everything under /debug requires a bearer token with the admin scope โ€” a heap or trace profile is a memory image that can contain request bodies and credentials, and expvar publishes the process command line. GATEWAY_ENV=production allows ENABLE_PPROF but warns at startup; unmount it once an investigation ends.

Bedrock / AWS credentialsโ€‹

AWS Bedrock is registered automatically when AWS_REGION, AWS_ACCESS_KEY_ID, or AWS_BEARER_TOKEN_BEDROCK is present.

  • AWS_REGION - AWS region for Bedrock.
  • AWS_ACCESS_KEY_ID - AWS access key (optional โ€” falls back to the instance role / default credential chain).
  • AWS_SECRET_ACCESS_KEY - AWS secret key.
  • AWS_SESSION_TOKEN - session token for temporary credentials (e.g. STS / assumed-role / SSO sessions). Required alongside the access key and secret when using temporary credentials.
  • AWS_BEARER_TOKEN_BEDROCK - a Bedrock bearer token, used instead of SigV4 keys.

For the full list of provider API-key variables (OpenAI, Anthropic, Gemini, and the rest of the 30 supported providers), see Provider configuration.

Tracing (OTEL_*)โ€‹

The gateway reads exactly two OTEL_* variables โ€” nothing else:

  • OTEL_EXPORTER_OTLP_ENDPOINT - OTLP collector base endpoint. Setting it alone turns tracing on and takes precedence over the config file's observability.tracing.endpoint. The base gets v1/traces appended under http/protobuf.
  • OTEL_EXPORTER_OTLP_TRACES_ENDPOINT - signal-specific traces endpoint, used verbatim (no path appended). It outranks OTEL_EXPORTER_OTLP_ENDPOINT when both are set.

Everything else about tracing โ€” including the sampler โ€” is config-only. observability.tracing.sample_ratio builds a ParentBased head sampler (default 1.0, so an inbound sampled trace is always followed); OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG have no effect on this gateway. OTEL_EXPORTER_OTLP_HEADERS reaches the exporter through the OTel SDK directly, not through the gateway. See the config reference for the full observability.tracing block and Monitoring for metrics and the OTLP exporter setup.

  • Configuration โ€” routing, plugins, and the observability.tracing config block
  • Monitoring โ€” metrics, health checks, and OTLP tracing setup
  • Auth โ€” MASTER_KEY, API keys, and dashboard sessions
  • Rate limiting โ€” the per-IP middleware vs. the rate-limit plugin
  • Provider configuration โ€” per-provider API keys and <PROVIDER>_BASE_URL