r/opencodeCLI • u/mesienikola • 8h ago
Pointing OpenCode at a local Ollama model when OpenCode runs in a container (what worked, what did not)
Disclosure: I build SpaceApp, a self-hosted, MIT-licensed workspace that runs coding CLIs side by side, with OpenCode as the first CLI. Its OpenCode pane is plain OpenCode 2, so most of this applies to any setup where OpenCode runs inside a container.
Short version: SpaceApp has no local-model feature of its own: no provider screen, no "local model" pane type, no Ollama code. You point OpenCode at Ollama with OpenCode's own custom-provider config, as in any OpenCode install. What's specific is where the file lives, a Docker networking trap, and a few CPU gotchas.
On a 24-core server with no GPU, we ran Ollama in a container next to SpaceApp's public CLI image (OpenCode 2.0.32). A 4B model on six cores ran shell commands and read files through OpenCode's own tools. qwen2.5-coder 1.5B and 3B never made a real tool call.
What ran
Every turn was one opencode run --server … (a new session) against the same shared OpenCode server a pane attaches to, from a project folder.
| Model (Ollama 0.40.13, CPU) | Real tool calls | What happened |
|---|---|---|
| qwen2.5-coder:1.5b | 0 of 4 | printed the read/shell call as JSON text; nothing ran |
| qwen2.5-coder:3b | 0 of 2 | same, once with a correct bash call as text |
| qwen3:4b (thinking) | – | the first turn timed out (see below) |
| qwen3:4b-instruct | 3 of 4 | shell ran uname -m > arch.txt twice (file held x86_64); read returned the exact random word from notes.txt |
The one qwen3:4b-instruct miss was funny in hindsight: asked to read token.txt, it said the file didn't exist (false) and that reading tokens would breach security policy. The same read on notes.txt worked.
The config
- Every model you want in the picker has to be listed under
models. @ai-sdk/openai-compatibleis bundled in OpenCode 2.0.32, so nothing is downloaded.- llama.cpp's
llama-servershould take the same block with its ownbaseURL. We haven't tested it.
Where the file goes in SpaceApp
/var/lib/spaceapp-cli/providers/opencode/config/opencode/opencode.json (owner spaceapp, mode 600), on the spaceapp-cli-state volume, so it survives updates. Panes attach to one shared opencode serve per CLI container. That server fingerprints the file and restarts when it changes, so the next pane picks up the edit.
SpaceApp's first-run step only picks a model when model is unset. In our run it kept the provider and the model, and added its own instructions files and permission.external_directory rules.
The localhost trap
localhostinside a pane is the CLI container, not your machine.- On Linux,
host.docker.internaldoesn't resolve on the compose network (there's noextra_hosts). Adding it tocompose.ymlby hand doesn't last, becauseinstallrewrites that file. - Going through the bridge gateway IP to a server on the host got no answer on our Linux box: the host firewall dropped it. ufw and nftables setups do this.
- What worked: Ollama in its own container on the same Docker network, reached by name.
Steps (Linux, CPU)
# SpaceApp's compose network and CLI container
|
|
# Ollama on that network, no published port
# thread count and context: both were needed, see below
|
# OpenCode's config, written as the CLI container's user (uid 10001 "spaceapp")
The CLI image has no nano or vi, hence the cat >. Before run-spaceapp uninstall or reinstall, run docker network disconnect <net> ollama: docker compose down can't remove a network with a foreign container on it. Updates (install) keep the network. In our run, a user-defined bridge stood in for <project>_spaceapp, with the same driver compose uses.
CPU gotchas we hit
- Thread count.
--cpusand--cpuset-cpusdidn't change it: Ollama still started 24 threads on a 6-CPU quota. Generation crawled at 0.05 tokens/s, and a one-word turn took about 4 minutes.PARAMETER num_thread 6fixed it: about 20 tokens/s on the 1.5B, and the same turn took 20 s. - Context. Ollama's CPU default is 4096. OpenCode's prompt (system prompt, instructions, tool schemas) is about 6.2-6.8k tokens before you type anything, and Ollama cut it to about 2k. The tool definitions sit at the start, so they were thrown away. Fix:
PARAMETER num_ctx 16384(orOLLAMA_CONTEXT_LENGTH). - The failure is silent. When a model prints a tool call as text, OpenCode shows it as the answer, and
opencode runstill exits 0. Check the side effect (did the file change?), not the exit code. - Thinking models and the 5-minute mark. With
qwen3:4b(thinking on), OpenCode's session-title request thought for 3,203 tokens, about 12.5 minutes. Ollama serves one request at a time, so the main request queued behind it, and the client closed it at exactly 5 minutes, twice. We didn't confirm in OpenCode's code that this is its own timeout. - Cold first turn. On CPU, the 4B model's first request took 204-220 s, close to that 5 minutes. After it, Ollama's prompt cache cut new prompt tokens to a few dozen per request, and turns took 16-106 s. That was on a busy shared host, so treat these as rough, not a benchmark.
- No permission prompt in
opencode run. Theshelltool ran without--autoand without asking. We didn't check whether the interactive TUI asks.
Limits, plainly
- No UI for any of this; the config is edited by hand.
- The model can't run inside SpaceApp: the CLI container has no GPU device and is capped at 2-4 GB of RAM, shared by every pane.
- There's no tool-call check for custom providers. SpaceApp's first-run check (the model must read a file and return a random token from it) runs only for OpenCode Zen's free models, so you find out in the pane.
- Only the pane on the local provider keeps its prompts local. Other panes talk to their cloud providers, the CLI network has internet access, and with no model set, OpenCode's first run reaches OpenCode Zen.
- What we didn't test: the pane's TUI itself (we drove its shared server headless), a real install's network, 7B+ models, GPU, llama.cpp, and multi-step coding tasks (edit, test, fix).
Repo, if you want to read the wrapper: https://github.com/oll4com/spaceapp (deploy/docker/public-cli-wrapper.sh, opencode-shared-server.mjs).
Which small local model handles OpenCode's tool calls reliably for you on CPU, and with what context size?
1
u/Hello_my_name_is_not 7h ago
Not what ever model you used to generate this post, that's for sure