llama-server, and in-house wrappers alike. Connecting one is configuration on both sides — no adapter, no SDK, no code.
What you get for doing it: every request to your own GPUs carries the same user identity, budget enforcement, rate limits, and audit record as a request to OpenAI or Anthropic. Your developers point at one endpoint and stop caring which side of the line a model lives on. And if your self-hosted capacity runs out, a model route can fall back to a hosted provider automatically — see Provider Failover.
Before you start
You’ll need three things:- A running model server, reachable over the network from Barndoor (see Making the server reachable — this is the step that trips people up).
- The API key you started that server with.
- Admin access to LLM Management in the Barndoor portal.
Step 1: Start your server so Barndoor can reach it
Two defaults bite on nearly every model server, and both must change before Barndoor can use it:- It listens on loopback only, so nothing off that machine can connect.
- It requires no credential, so anything that can connect may use your GPUs freely.
- vLLM
- Ollama
- Anything else
--api-key turns on bearer-token authentication for the /v1 paths. Treat the value as a secret: it is the only thing standing between your GPUs and anyone who can reach the port. Barndoor stores it encrypted and never exposes it to end users.Flags worth knowing about:--served-model-name — control the name clients use
--served-model-name — control the name clients use
vllm serve Qwen/Qwen3-8B reports Qwen/Qwen3-8B in /v1/models and expects that exact string in the model field of a request.--served-model-name qwen3-8b overrides that with something friendlier. Whatever you choose, it must match what you enable in Barndoor in Step 3 — the gateway passes the name through untouched.--enable-auto-tool-choice / --tool-call-parser — tool calling
--enable-auto-tool-choice / --tool-call-parser — tool calling
tool_calls when the server was started for it, and the correct parser depends on the model family:tools through unchanged either way — it can’t compensate for a server that wasn’t started for tool calling.--reasoning-parser — separate reasoning from the answer
--reasoning-parser — separate reasoning from the answer
--reasoning-parser qwen3 (or deepseek_r1, depending on the family) splits thinking tokens into their own field rather than leaving them inline in content. Recommended if your clients render responses directly to users.--max-model-len — cap context length
--max-model-len — cap context length
Making the server reachable
Barndoor connects to your server over the network like any other client, which means the gateway has to be able to route to it. This is independent of which server you run.- Barndoor SaaS
- Self-hosted Barndoor
app.barndoor.ai. You need to give it a routable address:- Put it behind your own load balancer or ingress with a public DNS name and TLS.
- Restrict who can reach that address — an IP allowlist for Barndoor’s egress addresses, or mutual TLS at your edge.
--api-keyis authentication, not network isolation.
Step 2: Add the provider
In the Barndoor portal, go to LLM Management → Providers → Add and choose Custom Provider — not one of the named vendor cards. That’s the right choice rather than a compromise. The named cards exist to carry facts about a vendor’s hosted endpoint: its URL, the models it serves, its per-token prices. None of those are knowable for a server you run yourself — a card for one could only ever hold a placeholder URL, an empty model list, and no pricing. The Custom Provider flow asks you for the three things that actually vary and assumes nothing else, which is why it works the same for every server in Step 1.GET {base_url}/v1/models with your key, timing out after 10 seconds. The result is recorded as the provider’s health:
Step 3: Enable the models
Open the provider and choose Add Models, then type your model name in. A custom provider has no catalog to pick from, which is the honest state of affairs — only your server knows what it loaded. The name must match what/v1/models reports, exactly. Take it from the server rather than from memory:
Qwen/Qwen3-8B) unless you set --served-model-name, in which case it’s whatever you chose. Copy it character for character — Qwen/Qwen3-8B and qwen3-8b are different models as far as the gateway is concerned, and a mismatch surfaces only when the first request 404s.
Models added this way carry a Custom badge. That’s provenance, not a warning: you supplied the name rather than picking it from a vendor catalog. It’s also the first place to look when requests to a model fail.
Step 4: Create a model route
Models attached to a provider aren’t yet callable by a client name. Under Model Routes → Create Route, define the alias your developers will actually use — sayqwen3 — and point it at the model you just enabled.
This indirection is what lets you move traffic later without touching a single client: repoint the route at a bigger GPU node, or add a hosted provider as a second target so requests spill over when your own capacity is saturated.
Step 5: Verify the connection
Test in layers. Each one isolates a different failure, and a green result at one layer tells you nothing about the next — so resist skipping ahead when something breaks. Almost every failed setup is diagnosed by finding the lowest layer that fails.Your server is serving — on the server host
It's reachable from somewhere else
Barndoor can reach it
GET {base_url}/v1/models with your stored key and writes the result to the provider’s health badge.To re-test after changing something on your side, open the provider and Save again without editing any field. Health is only re-evaluated on create and update, so a server you just fixed keeps its stale badge until you do.The model is exposed to a caller
A request completes — both ways
"stream": true added. Streaming goes through a different path — an SSE relay with its own idle timeout — and it’s what coding assistants and chat UIs actually use. A passing non-streaming request is not evidence that streaming works.data: chunks, a final chunk carrying usage, then data: [DONE]. The usage chunk is what makes a streamed request billable and countable — if it’s missing, tokens won’t reach your reports.Governance actually applied
Cost reporting for self-hosted models
Self-hosted inference has no market rate — the cost is your own GPU time — so Barndoor ships no default pricing for self-hosted models. Until you say otherwise, usage reporting counts tokens accurately and reports the cost as zero, and models show an Unpriced badge. If you want self-hosted traffic to show up in cost reports and count against budgets, set your own rates under Model Pricing. A reasonable approach is to divide the fully-loaded hourly cost of the instance by the tokens it produces in that hour, and enter the result as an input and output rate. It doesn’t need to be exact to be useful: even a rough number makes “what did this team actually consume” answerable, and lets a token budget act as a real ceiling. See Managing Model Pricing for the mechanics.Troubleshooting
could not reach upstream provider
could not reach upstream provider
- vLLM is bound to loopback — restart it with
--host 0.0.0.0. - A firewall or security group blocks the port from Barndoor’s side.
- You’re on Barndoor SaaS and the address is private. See Making the server reachable.
- You’re using
https://with a self-signed or private-CA certificate.
connection to upstream timed out after 10s
connection to upstream timed out after 10s
upstream rejected the credentials (HTTP 401)
upstream rejected the credentials (HTTP 401)
Provider saved as Unverified
Provider saved as Unverified
/v1 — remove it and save again. If your base URL is already the host root, check whether a reverse proxy in front of your server is rewriting or blocking /v1/models; the gateway treats an inaccessible model list as inconclusive rather than broken, which is why this is a distinct state from unhealthy.Requests fail with an unknown-model error
Requests fail with an unknown-model error
curl http://<host>:8000/v1/models — the id there is what the model field must contain, letter for letter. If you added --served-model-name after configuring Barndoor, the enabled name is now stale.Tool calls come back as prose
Tool calls come back as prose
--enable-auto-tool-choice and a --tool-call-parser matching the model family. Barndoor forwards the tools array unchanged; it cannot synthesize structured tool calls from a server that isn’t producing them.Current limits
- Chat completions and streaming are the supported surface. Many servers also expose embeddings, completions, and the Responses API, but those are not verified through the gateway on this path.
- Tool calling and reasoning output depend entirely on how you started the server, so Barndoor makes no promise about them on your behalf. They pass through when your server produces them.
- Barndoor SaaS cannot reach a server on a private network. There is no tunnel or agent for this today; the server needs a routable address, or Barndoor needs to run inside your infrastructure.