Provider configuration
Provider credentials are supplied through environment variables and never live in config.yaml. A config target only names a provider by its virtual_key; the credential that activates that provider comes from the environment:
targets:
- virtual_key: openai # names the provider โ credentials come from OPENAI_API_KEY
A provider is auto-registered when its required environment variables are present at startup. No provider entry needs editing in code and no key is ever written to disk in the config.
Setting a provider's env vars registers it, but it only serves traffic if a targets[] entry names its virtual_key. A request for a model no configured target owns returns 404 model_not_found even when the provider is registered and healthy.
The required-key gate runs whenever a provider is built โ whether credentials come from environment variables or from a programmatic config map. A provider is never constructed without its credential: on the environment path a missing required var skips the provider silently; on the programmatic path it is an error.
Provider credentials are read directly from their own variables. Elsewhere in config โ plugins[].config, mcp_servers[].headers / env, and observability.exporters[].config โ you can reference an environment variable with ${VAR}. Only the braced form ${NAME} is a reference (a bare $ is literal data, so pa$$w0rd survives byte-for-byte). References resolve when the component is constructed, not at config load, so a secret never reaches the config-history store or GET /admin/config; an undefined variable is a startup error that names every missing variable.
The base-URL ruleโ
Since v1.4.0, <PROVIDER>_BASE_URL is the API root, used verbatim โ version segment included. Each surface (chat, streaming, embeddings, images, model discovery, /v1/* pass-through) appends only its operation path to that root, and the root is resolved once, when the provider is constructed. Write it exactly as the vendor documents it:
# Reaches https://proxy.example.com/v1/chat/completions and .../v1/embeddings
export OPENAI_BASE_URL=https://proxy.example.com/v1
- Include the version segment. Write
https://api.groq.com/openai/v1, not.../openai. - A base with no path at all is the one case with a safety net: it resolves to the provider's own default version segment (
/v1for OpenAI-wire providers,/v1betafor Gemini). Sohttp://host:9901andhttp://host:9901/v1are equivalent. A base that carries any path is taken as written. - Userinfo travels with the host:
https://user:pass@proxy.example.comreaches the proxy authenticated. - A query string or fragment is refused at startup โ an operation path is appended to the root, so a query would bury the operation.
Host-root exceptions. Three providers are configured with a host rather than an API root, because the value genuinely is not one:
| Provider | Variable | Write it as |
|---|---|---|
| Cohere | COHERE_BASE_URL | the host โ https://api.cohere.com (chat is /v2/chat, embeddings /v1/embed, so no single API root exists) |
| Ollama | OLLAMA_HOST | the server root โ http://localhost:11434 (OpenAI surface at /v1, native API at /api) |
| Azure AI Foundry | AZURE_FOUNDRY_ENDPOINT | the resource host โ https://<resource>.services.ai.azure.com (the gateway appends Azure's fixed /openai/v1) |
DATABRICKS_HOST, AZURE_OPENAI_ENDPOINT, and HUGGING_FACE_ENDPOINT are resource hosts too โ not _BASE_URL overrides. The provider builds the vendor's fixed surface path beneath them.
OpenAI-compatible providersโ
These providers register with a single API key and accept an optional <PROVIDER>_BASE_URL override (see the base-URL rule above):
export OPENAI_API_KEY=sk-...
export GROQ_API_KEY=gsk_...
export DEEPSEEK_API_KEY=...
# ...and so on for any provider below
| Provider | API key env var | Base-URL override | Default API root |
|---|---|---|---|
| AI21 Labs | AI21_API_KEY | AI21_BASE_URL | https://api.ai21.com/studio/v1 |
| Cerebras | CEREBRAS_API_KEY | CEREBRAS_BASE_URL | https://api.cerebras.ai/v1 |
| DeepInfra | DEEPINFRA_API_KEY | DEEPINFRA_BASE_URL | https://api.deepinfra.com/v1/openai |
| DeepSeek | DEEPSEEK_API_KEY | DEEPSEEK_BASE_URL | https://api.deepseek.com/v1 |
| Fireworks AI | FIREWORKS_API_KEY | FIREWORKS_BASE_URL | https://api.fireworks.ai/inference/v1 |
| Groq | GROQ_API_KEY | GROQ_BASE_URL | https://api.groq.com/openai/v1 |
| Mistral AI | MISTRAL_API_KEY | MISTRAL_BASE_URL | https://api.mistral.ai/v1 |
| Moonshot AI | MOONSHOT_API_KEY | MOONSHOT_BASE_URL | https://api.moonshot.ai/v1 |
| Novita | NOVITA_API_KEY | NOVITA_BASE_URL | https://api.novita.ai/openai/v1 |
| NVIDIA NIM | NVIDIA_NIM_API_KEY | NVIDIA_NIM_BASE_URL | https://integrate.api.nvidia.com/v1 |
| OpenAI | OPENAI_API_KEY | OPENAI_BASE_URL | https://api.openai.com/v1 |
| OpenRouter | OPENROUTER_API_KEY | OPENROUTER_BASE_URL | https://openrouter.ai/api/v1 |
| Perplexity | PERPLEXITY_API_KEY | PERPLEXITY_BASE_URL | https://api.perplexity.ai |
| Qwen (DashScope) | QWEN_API_KEY | QWEN_BASE_URL | https://dashscope-intl.aliyuncs.com/compatible-mode/v1 |
| SambaNova | SAMBANOVA_API_KEY | SAMBANOVA_BASE_URL | https://api.sambanova.ai/v1 |
| Together AI | TOGETHER_API_KEY | TOGETHER_BASE_URL | https://api.together.ai/v1 |
| xAI (Grok) | XAI_API_KEY | XAI_BASE_URL | https://api.x.ai/v1 |
Anthropicโ
A single API key. ANTHROPIC_BASE_URL is an optional verbatim API-root override (default https://api.anthropic.com/v1). Anthropic uses its native Messages wire, so /v1/* pass-through proxying returns 501 by design.
export ANTHROPIC_API_KEY=sk-ant-...
Google Geminiโ
A single API key. GEMINI_BASE_URL is an optional verbatim override; a path-less base resolves to /v1beta (the provider's default). Gemini uses its native generateContent wire, so /v1/* pass-through returns 501.
export GEMINI_API_KEY=...
Cohereโ
A single API key. COHERE_BASE_URL is a host-root override โ write only the host (see host-root exceptions). Cohere uses its native v2 wire; the gateway's /v1/rerank endpoint follows the Cohere-v2 contract, and /v1/* pass-through returns 501.
export COHERE_API_KEY=...
export COHERE_BASE_URL=https://api.cohere.com # optional; host only
Hugging Faceโ
A single API key. The override variable is HUGGING_FACE_ENDPOINT (config key base_url), not HUGGING_FACE_BASE_URL โ it points at a dedicated Inference Endpoint, a resource host. Default root: https://router.huggingface.co/v1.
export HUGGING_FACE_API_KEY=hf_...
export HUGGING_FACE_ENDPOINT=https://router.huggingface.co/v1 # optional
Azure OpenAIโ
Requires the resource endpoint and a deployment name in addition to the key. The API key is sent as the api-key header. The deployment name is the model set โ this is the only provider with a required deployment config key.
export AZURE_OPENAI_API_KEY=...
export AZURE_OPENAI_ENDPOINT=https://<resource>.openai.azure.com # resource host
export AZURE_OPENAI_DEPLOYMENT=gpt-4o # required
export AZURE_OPENAI_API_VERSION=2024-10-21 # optional; default 2024-10-21
Request URLs are built as {endpoint}/openai/deployments/{deployment}/{op}?api-version=...; the batch surface uses the resource's /openai/v1 root. Pass-through proxying returns 501 (native wire).
Azure AI Foundryโ
Requires the resource host. The endpoint is a host, not an API root โ the gateway appends Azure's fixed GA /openai/v1 route.
export AZURE_FOUNDRY_API_KEY=...
export AZURE_FOUNDRY_ENDPOINT=https://<resource>.services.ai.azure.com # resource host
export AZURE_FOUNDRY_API_VERSION=... # optional
AWS Bedrockโ
Bedrock is built on aws-sdk-go-v2, so it has no HTTP base URL. It is considered configured when any of AWS_REGION, AWS_ACCESS_KEY_ID, or AWS_BEARER_TOKEN_BEDROCK is set โ three alternative credential modes. Region defaults to us-east-1.
# 1. API-key (bearer) auth
export AWS_BEARER_TOKEN_BEDROCK=...
export AWS_REGION=us-east-1 # optional; defaults to us-east-1
# 2. Static credentials (SigV4)
export AWS_ACCESS_KEY_ID=AKIA...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=... # optional
export AWS_REGION=us-east-1
# 3. Instance role / credential chain
export AWS_REGION=us-east-1
Bedrock uses its native InvokeModel API; pass-through proxying returns 501.
Google Vertex AIโ
Requires the project ID (the activation gate) and region โ VERTEX_AI_REGION is required once VERTEX_AI_PROJECT_ID is set, or construction errors. Authentication resolves through one of three paths:
export VERTEX_AI_PROJECT_ID=my-gcp-project
export VERTEX_AI_REGION=us-central1
# then one of:
export VERTEX_AI_API_KEY=...
# or a service-account JSON string or path (JWT exchanged for the cloud-platform scope):
export VERTEX_AI_SERVICE_ACCOUNT_JSON='{"type":"service_account",...}'
# or leave both unset to use Application Default Credentials / workload identity
The base URL is built from config as https://{region}-aiplatform.googleapis.com/v1/projects/{project_id}/locations/{region}/endpoints/openapi. Native wire; pass-through returns 501.
Cloudflare Workers AIโ
The only provider that requires an account ID. CLOUDFLARE_BASE_URL is an optional override; the default root interpolates the account ID.
export CLOUDFLARE_API_KEY=...
export CLOUDFLARE_ACCOUNT_ID=...
Default API root: https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/v1.
Databricksโ
Requires the workspace host and a token. DATABRICKS_TOKEN maps to config key api_key; DATABRICKS_HOST is a resource host (config key base_url) from which the gateway builds the vendor's fixed surface path.
export DATABRICKS_TOKEN=dapi...
export DATABRICKS_HOST=https://<workspace>.cloud.databricks.com # workspace host
Replicateโ
The model set comes from config, so declare the models you intend to serve. Optional REPLICATE_TEXT_MODELS / REPLICATE_IMAGE_MODELS are comma-separated lists (config keys text_models / image_models).
export REPLICATE_API_TOKEN=r8_...
export REPLICATE_TEXT_MODELS=meta/llama-3-8b-instruct,mistralai/mistral-7b-instruct-v0.2
export REPLICATE_IMAGE_MODELS=black-forest-labs/flux-schnell
Replicate authenticates with REPLICATE_API_TOKEN (not _API_KEY); its config key is api_token, not api_key. Native wire โ pass-through returns 501.
Ollamaโ
Local Ollama has no API key. OLLAMA_HOST (config key host) is both the activation gate and a server root, not an API root (see host-root exceptions). Ollama serves any model it has pulled, so FERRO_OLLAMA_MODELS only narrows what /v1/models advertises.
export OLLAMA_HOST=http://localhost:11434
export FERRO_OLLAMA_MODELS=llama3.2,gpt-oss:20b,mistral # optional; narrows /v1/models
FERRO_OLLAMA_MODELSOLLAMA_MODELS is Ollama's own variable for its models directory ($HOME/.ollama/models), so it must not be used to list models here โ it is deprecated in this gateway and read for one more release with a startup WARN, and a path-shaped value (no comma) is dropped with a WARN. When both are set, FERRO_OLLAMA_MODELS wins.
Ollama Cloudโ
The hosted Ollama service, which does require an API key. OLLAMA_CLOUD_BASE_URL (default https://ollama.com/v1) and OLLAMA_CLOUD_MODELS are optional.
export OLLAMA_API_KEY=...
export OLLAMA_CLOUD_MODELS=gpt-oss:20b # optional; comma-separated
Ollama Cloud is the only provider with no /v1/* pass-through surface.
Verify configured providersโ
After starting the gateway, confirm which providers registered:
# Unauthenticated: per-provider status, model counts, and circuit state
curl http://localhost:8080/health
# Registered models grouped by provider (needs a bearer token โ an API key or MASTER_KEY)
curl -H "Authorization: Bearer $MASTER_KEY" http://localhost:8080/v1/models
/health stays unauthenticated and reports provider names, model counts, and circuit state. /v1/models and the rest of /v1/* require a bearer token unless ALLOW_UNAUTHENTICATED_PROXY=true.
Relatedโ
- Providers overview โ endpoint matrix and supported surfaces per provider
- Configuration reference โ the full config schema
- Server settings โ env vars, timeouts, and limits
- Authentication โ bearer tokens, scopes, and
MASTER_KEY - Routing โ targets, strategies, and failover