LibreChat: Zukko as a custom endpoint in librechat.yaml
LibreChat connects third-party providers through librechat.yaml. Here is a ready config for Zukko, the Docker mount and the usual errors.
5 min · · development · автоматизация
LibreChat is a self-hosted chat that supports many providers. Third-party OpenAI-compatible APIs are added as a custom endpoint in librechat.yaml. Below is a working config for Zukko, the key and Docker setup, a cost estimate, and the errors that most often keep the endpoint from showing up in the UI.
How the setup works
According to the LibreChat docs, a custom endpoint involves three files:
librechat.yaml— the endpoint definition: name, API address, models, display options;.env— secrets thatlibrechat.yamlreferences via${VARIABLE_NAME};docker-compose.override.yml— mountslibrechat.yamlinto the container (Docker only).
librechat.yaml and .env live in the LibreChat project root. You can point to a different config path with the CONFIG_PATH variable.
LibreChat custom endpoint for Zukko: librechat.yaml
version: 1.3.17 # check the current config version in the LibreChat docs
cache: true
endpoints:
custom:
- name: "Zukko"
apiKey: "${ZUKKO_API_KEY}"
baseURL: "https://api.zukko.pro/v1"
models:
default: ["claude-sonnet-5", "claude-opus-5-5", "gpt-5.6-sol", "gpt-5.6-luna", "glm-5.3-flash"]
fetch: true
titleConvo: true
titleModel: "glm-5.3-flash"
modelDisplayLabel: "Zukko"Field by field:
customis a list, so every endpoint starts with a dash. A common mistake is writing it as a plain mapping without- name:.name— the label shown in the endpoint selector.apiKey— a reference to an environment variable. The LibreChat docs recommend this over putting the key in the file.baseURL— the Zukko address, with/v1.models.default— an array of models that is always shown. It's required even whenfetchis on.models.fetch: true— LibreChat asks the API for its model list. Zukko serves it atGET /v1/models, so new models appear without editing the config.titleConvoandtitleModel— LibreChat generates conversation titles itself. For this background task the cheapest model makes sense.modelDisplayLabel— the model label in the chat.
The key in .env
ZUKKO_API_KEY=fetch_your_keyDon't put your Zukko key in OPENAI_API_KEY: that variable belongs to the built-in OpenAI endpoint, and LibreChat would send it to OpenAI's servers. The LibreChat docs warn about a similar name clash for OpenRouter, so use your own variable names for custom endpoints.
Docker setup
For the container to see librechat.yaml, copy the example override file:
cp docker-compose.override.yml.example docker-compose.override.ymland uncomment the config mount:
services:
api:
volumes:
- type: bind
source: ./librechat.yaml
target: /app/librechat.yamlDocker Compose merges the override file with the main docker-compose.yml automatically, so your changes survive LibreChat updates.
After any change to librechat.yaml or .env, restart LibreChat:
docker compose down && docker compose up -dIf you run it locally without Docker, stop the process (Ctrl+C) and run npm run backend again.
Verification
- Open LibreChat and find Zukko in the endpoint list.
- Pick a model, for example
claude-sonnet-5, and send a short message. - If the endpoint is missing, check the logs:
docker compose logs api. You can also validate the config with the YAML validator linked from the LibreChat docs.
Two keys: split the spending
If LibreChat is used both for work and for experiments, create two keys with different limits in the Zukko dashboard and define two endpoints:
endpoints:
custom:
- name: "Zukko Work"
apiKey: "${ZUKKO_WORK_KEY}"
baseURL: "https://api.zukko.pro/v1"
models:
default: ["claude-sonnet-5", "claude-opus-5-5"]
fetch: false
- name: "Zukko Lab"
apiKey: "${ZUKKO_LAB_KEY}"
baseURL: "https://api.zukko.pro/v1"
models:
default: ["glm-5.3-flash", "gpt-5.6-luna"]
fetch: falseWith fetch: false users only see the listed models. To be safe, restrict the key itself too: every key in the dashboard has an allowed-models list and daily and monthly limits (see the key limits documentation).
Model choice and cost
| Model | Input / output, $ per 1M tokens | Context |
|---|---|---|
claude-opus-5-5 | 0.3 / 0.3 | 1M |
claude-sonnet-5 | 0.3 / 0.3 | 1M |
gpt-5.6-sol | 0.3 / 0.3 | 1M |
gpt-5.6-luna | 0.1 / 0.1 | 1M |
glm-5.3 | 0.1 / 0.1 | 128K |
glm-5.3-flash | 0.04 / 0.04 | 128K |
What a conversation title costs: say the title request takes 500 tokens including the answer. On claude-sonnet-5 that's 500 × $0.3 / 1,000,000 = $0.00015; on glm-5.3-flash, 500 × $0.04 / 1,000,000 = $0.00002. Small, but it adds up with every new conversation, so set titleModel explicitly.
For the main conversations, choose a model based on your own tasks: take 10–20 typical requests, run them on two or three models, and compare the answers and spending in the dashboard's "Usage" section. For the current model list, call GET https://api.zukko.pro/v1/models or open the "Models" page in the dashboard.
Common problems
Zukko doesn't appear in the endpoint list
Check in order: the file is in the project root (or CONFIG_PATH is set); in Docker it's mounted via the override file; the YAML has no indentation errors; the endpoint has all required fields — name, apiKey, baseURL, models.default. LibreChat may silently skip an incomplete block, so check docker compose logs api.
401: invalid key
The variable name in .env doesn't match the one in apiKey, or LibreChat wasn't restarted after editing .env. The key must start with fetch_ and be copied in full.
404
baseURL is missing /v1, or has an extra /chat/completions. It must be exactly https://api.zukko.pro/v1.
400 on Claude models
claude-sonnet-5 and claude-opus-5-5 may reject a request with a 400 error if temperature is anything other than 1 or top_p is below 0.99. By default LibreChat doesn't send these parameters for custom endpoints, but users can set them in the conversation settings. For an endpoint with only Claude models you can add dropParams: ["temperature", "top_p"], and LibreChat will strip them from requests.
402 and 403
402 means the balance ran out; top it up in the dashboard. 403 model_not_allowed_for_key means the model isn't on this key's allowed list.
FAQ
Can each user enter their own key?
Yes. The LibreChat docs allow apiKey: "user_provided", in which case users enter the key in the UI themselves. That way everyone pays from their own Zukko balance.
Can I connect Zukko using the Anthropic format?
LibreChat supports provider: "anthropic" for custom endpoints, and Zukko accepts the Anthropic format too. But with the native Anthropic client the address goes without /v1, and the model list has to be written out by hand. The OpenAI-compatible option is simpler: the gateway converts requests to Claude for you.
Where can I see spending?
In the Zukko dashboard under "Usage". If you use separate keys for different endpoints, spending is split by key.
Where do I start?
Create a key in the Zukko dashboard, put it in .env, and copy the config above. For more on the API formats, see the Chat Completions documentation.
zukko
Pay 25×
less
for Claude Code & ChatGPT
One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.
Related articles
- Translating subtitles with a neural network: SRT without broken timecodesThe main risk when translating SRT with a neural network is a broken file structure. A scheme where timecodes never reach the model, and a Python script with validation.
- Editing texts with a neural network: proofreading, style and cuttingDifferent kinds of editing need different prompts. Wording for proofreading, style and cutting, how to get edits with explanations and how not to lose the author's voice.
- A neural network for writing scripts: Bash, PowerShell and PythonA model writes scripts for routine fast, but running them blindly is risky. How to state the task, demand a safe dry-run mode and check the result before launch.
- Dify in Docker: installation, first-run setup and connecting modelsInstall Dify with Docker Compose, create the admin account and connect Claude, GPT and DeepSeek models on one key. Plus what to do when containers will not start.