Containing OpenCode with Podman
Posted on Sat 18 April 2026 in hints-and-kinks • 3 min read
OpenCode is an MIT-licensed agentic coding assistant. It’s not completely trivial to run it in rootless Podman, but it can be done. Here’s how.
The OpenCode container image
Although the OpenCode documentation makes it a bit hard to find, an official Docker image for OpenCode does exist, and is available from the GitHub Container Registry (GHCR).
It runs in TUI mode, out of the box, with podman run, like so:
podman run -it ghcr.io/anomalyco/opencode
Images are released along with new OpenCode versions, so if you don’t want the latest, you might instead want to run:
podman run -it ghcr.io/anomalyco/opencode:1.4.5
Rootless Podman
I’ve written about how I manage services with systemd and podman in rootless mode before; see this article for details.
The remainder of this article assumes the same setup.
The objective
What I want to do is this:
- Run OpenCode in a rootless Podman container that I can manage as a user-mode systemd service.
- Selectively give OpenCode access to a directory containing Git repos.
- Ensure that OpenCode has access to those files using my user identity, the way the OpenCode TUI would if I ran the
opencodebinary directly (without a container). - Define everything in a Docker Compose configuration.1
Obviously, the TUI itself won’t be very helpful for that purpose.
However, OpenCode also comes with a very helpful server mode, including a web-based GUI, and this we can containerize rather well.
Considerations
There’s a few things one needs to know to make this work.
Default config file paths
In a default configuration, OpenCode needs access to the following files and directories, in addition to the working directory with the code I want it to modify:
~/.config/opencode/opencode.jsonor~/.config/opencode/opencode.jsonc: OpenCode’s main configuration file.~/.local/share/opencode: Directory for logs, and OpenCode’s SQLite database.~/.local/state/opencode: Directory for lock files and other state information.~/.cache/opencode: Cache directory.
Thus, containerized OpenCode requires that I mount these paths into my container.
OpenCode in server mode
When running in server mode, OpenCode by default listens on localhost only.
Thus, if I want to run it in a container and port-forward its server, it’s necessary to pre-create the ~/.config/opencode/opencode.json file and populate it.
Like so:
{
"$schema": "https://opencode.ai/config.json",
"server": {
"port": 4096,
"hostname": "0.0.0.0",
"cors": [
"http://localhost:4096"
]
}
}
Podman with PODMAN_USERNS=keep-id
When you run Podman with PODMAN_USERNS=keep-id, as I normally do, Podman
- makes sure that the user ID and name in the container match the UID and name of the host user that invokes the container,
- sets that user’s home directory in the container to whatever
--workdiroption it was invoked with (or/if no such option was given), - injects a line to the above effect into
/etc/passwdin the container.
Putting it all together
With all the above in mind, I can use this Compose configuration:
services:
opencode:
image: ghcr.io/anomalyco/opencode:1.4.7
container_name: opencode
volumes:
- ~/.local/share/opencode:/home/coder/.local/share/opencode
- ~/.local/state/opencode:/home/coder/.local/state/opencode
- ~/.config/opencode/opencode.json:/home/coder/.config/opencode/opencode.json
- ~/.cache/opencode:/home/coder/.cache/opencode
- ~/coding/git:/home/coder/git
working_dir: "/home/coder"
command: serve
ports:
- "127.0.0.1:4096:4096"
environment:
- 'SHELLCHECK_EXTERNAL_SOURCES=false'
restart: unless-stopped
I can then invoke this like so:
PODMAN_USERNS=keep-id podman compose up
With this,
- My host UID is mapped to the same UID in the container, meaning all file access on the host and in the container use the same credentials.
- My home directory inside becomes
/home/coderinside the container. - My OpenCode configuration files and state directory are accessible to OpenCode in the container.
- My home directory’s
coding/gitsubdirectory (assuming that’s where all my Git checkouts live) becomes/home/coder/gitin the container. If I wanted to be even more restrictive, I could mount just a single Git checkout directory into that container path. - The OpenCode web server is available on my host as http://localhost:4096.
Setting the environment variable SHELLCHECK_EXTERNAL_SOURCES=false is a workaround for the OpenCode issue 5363.
-
The reason I want to use the Docker Compose format is simplicity and personal preference. Skipping this layer, and using Podman Quadlets instead, would also be an option. ↩