Saudações.
Hoje vou ensinar como enlatei o OpenCode em um container e criei um enxame de programadores automatizados.
Pré-requisitos:
- Servidor, VPS ou VM com Linux;
- Docker;
1 – Sobre o OpenCode
O OC é um agente de IA especializado e refinado para planejar (plan) e construir (build) softwares.
Ele é uma “versão gratuita e aberta do Claude Code” e se tornou muito famoso ser simples e poderoso.
Embora essa seja a principal função dele, você pode usá-lo como agente genérico para qualquer tarefa.
É normal rodar vários agentes ao mesmo tempo, basta que para cada projeto você abra uma tela no terminal, vá até a pasta do seu projeto e rode o comando “opencode“.
Ele é executado de duas formas:
- Terminal do Agente: Ao rodar o comando “opencode” ele inicia um terminal moderno para interação com você, o usuário, nesse terminal você insere o prompt ou comandos específicos que iniciam com “/”;
- Comando para o Agente: Permite acionar o comando “opencode” com argumentos específicos para tarefas onshot.
Para funcionar ele precisa:
- Acesso a Internet durante a execução;
- Chave de API para algum modelo de IA.
Links:
- Projeto OpenCode: https://opencode.ai/;
- Projeto no Github: https://github.com/anomalyco/opencode;
- Documentação: https://opencode.ai/docs/pt-br/
Eu particularmente não quero amarrá-lo ao meu notebook nem acessar um terminal gráfico remotamente. Ao enlatar o OpenCode em container eu ganho a liberdade de ter um container para cada projeto e acessá-lo puramente por SSH de qualquer lugar, alem de automatizar execuções com gatilhos próprios.
2 – OpenCode no Docker
Essa parte foi um pouco difícil. Por ser feito em Javascript/TypeScript e por padrão rodando no framework do nodejs o OpenCode espalha arquivos em vários lugares.
Publiquei a imagem no Docker Hub em tmsoftbrasil/opencode:latest, logo você pode pular esse capítulo.
Leia os tópicos abaixo para entender a engenharia da imagem.
2.1 – Caminhos e volumes
Esses caminhos precisam ser mapeados e direcionado para volumes, assim o container separa:
- Programas: Os binários e bibliotecas de códigos;
- Dados: Configurações e informações acumuladas pelo OC;
- Cache: Arquivos gerados para acelerar a execução;
- Workspace: Projeto de código onde serão produzidos os artefatos do agente, como códigos, documentações, etc;
Caminhos que configurei:
| Caminho dentro do container | Natureza | Persistência recomendada |
|---|---|---|
/data/config/opencode | Config global, opencode.json, tui.json, agents, commands, plugins, skills | Persistente |
/data/share/opencode/auth.json | Credenciais, chaves de API, OAuth | Persistente e sensível |
/data/share/opencode/mcp-auth.json | Tokens OAuth de servidores MCP | Persistente e sensível |
/data/share/opencode/opencode.db | Banco SQLite observado por você | Persistente |
/data/share/opencode/log | Logs do OpenCode | Persistente por padrão |
/data/home/opencode | HOME do usuário, fallback para ferramentas que gravam em ~ | Persistente |
/data/npm-global | Pacotes npm globais instalados em runtime | Persistente |
/cache/npm | Cache e logs do npm | Descartável |
/cache/xdg/opencode | Cache XDG do OpenCode | Descartável |
/cache/tmp | TMPDIR e /tmp, inclusive .so temporário | Descartável |
/cache/opencode-config-node_modules | node_modules gerado em config/plugins | Descartável, recriável |
/workspace | Código do projeto | Bind mount do host |
2.2 – Arquivos para construir a imagem
Dockerfile da imagem:
# syntax=docker/dockerfile:1.7
# Base Debian Linux
FROM debian:bookworm-slim
ARG APP_UID=1000
ARG APP_GID=1000
# Versao do OpenCode, usar latest ou versao especifica
# docker buikd ... --build-arg OPENCODE_VERSION=1.x.y
ARG OPENCODE_VERSION=latest
ENV DEBIAN_FRONTEND=noninteractive \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8
# Pacotes base:
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
bash \
ca-certificates \
curl \
wget \
git \
openssh-client \
nodejs \
npm \
brotli \
tar \
xz-utils \
zstd \
unzip \
tzdata \
openssl \
gosu \
less \
procps \
jq \
sqlite3 \
ripgrep \
; \
rm -rf /var/lib/apt/lists/*
# Instala OpenCode via npm
RUN set -eux; \
npm install -g "opencode-ai@${OPENCODE_VERSION}"; \
opencode --version
# Usuario não-root para rodar o agente.
# A HOME real sera em /data, para persistir tudo que for config/
RUN \
set -eux; \
groupadd -g "${APP_GID}" opencode; \
useradd -u "${APP_UID}" -g "${APP_GID}" \
-d /data/home/opencode -s /bin/bash opencode; \
\
mkdir -p /data /cache /workspace /cache/tmp /root/.local; \
chmod 0755 /data /cache /workspace; \
chmod 1777 /cache/tmp
# Redireciona /tmp para /cache/tmp, inclusive para arquivos .so temporarios.
RUN \
rm -rf /tmp; \
ln -s /cache/tmp /tmp
# Criar links de /root para os volumes caso rode processos como root dentro do container
RUN \
rm -rf /root/.config /root/.cache /root/.npm /root/.local/share; \
ln -s /data/config /root/.config; \
ln -s /data/share /root/.local/share; \
ln -s /cache/xdg /root/.cache; \
ln -s /cache/npm /root/.npm
# Instalar script de entrypoint
COPY rootfs/opt/entrypoint.sh /opt/entrypoint.sh
RUN chmod +x /opt/entrypoint.sh
# Variaveis centrais:
# - XDG_CONFIG_HOME e XDG_DATA_HOME em /data
# - XDG_CACHE_HOME, npm cache e TMPDIR em /cache
# - NO_PROXY para evitar que o servidor local do TUI passe por proxy
ENV HOME=/data/home/opencode \
XDG_CONFIG_HOME=/data/config \
XDG_DATA_HOME=/data/share \
XDG_CACHE_HOME=/cache/xdg \
OPENCODE_CONFIG_DIR=/data/config/opencode \
NPM_CONFIG_CACHE=/cache/npm \
NPM_CONFIG_PREFIX=/data/npm-global \
TMPDIR=/cache/tmp \
NO_PROXY=localhost,127.0.0.1 \
PATH=/data/npm-global/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# Diretorio padrao inicial
WORKDIR /workspace
# Volumes anonimos (default, requer mapeamento no run)
VOLUME ["/data", "/cache", "/workspace"]
# Entrypoint
ENTRYPOINT ["/opt/entrypoint.sh"]
# Comando padrao
CMD ["bash"]
Script de Entrypoint:
#!/bin/bash
set -euo pipefail;
# Env defaults
export HOME="${HOME:-/data/home/opencode}";
export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-/data/config}";
export XDG_DATA_HOME="${XDG_DATA_HOME:-/data/share}";
export XDG_CACHE_HOME="${XDG_CACHE_HOME:-/cache/xdg}";
export OPENCODE_CONFIG_DIR="${OPENCODE_CONFIG_DIR:-/data/config/opencode}";
export NPM_CONFIG_CACHE="${NPM_CONFIG_CACHE:-/cache/npm}";
export NPM_CONFIG_PREFIX="${NPM_CONFIG_PREFIX:-/data/npm-global}";
export TMPDIR="${TMPDIR:-/cache/tmp}";
export NO_PROXY="${NO_PROXY:-localhost,127.0.0.1}";
# Criar link simbolico
safe_link() {
local src="$1";
local dst="$2";
if [ -L "$dst" ] || [ ! -e "$dst" ]; then
ln -sfnT "$src" "$dst";
fi;
};
# Garantir diretorios usados
mkdir -p \
"$HOME" \
"$HOME/.local" \
"$XDG_CONFIG_HOME/opencode" \
"$XDG_DATA_HOME/opencode" \
"$XDG_CACHE_HOME/opencode" \
"$NPM_CONFIG_CACHE" \
"$NPM_CONFIG_PREFIX/bin" \
"$TMPDIR" \
/cache/opencode-config-node_modules \
/workspace;
# Ajustes de permissoes
chmod 700 "$XDG_DATA_HOME/opencode" || true;
chmod 1777 "$TMPDIR" || true;
# Compatibilidade para softwares que ignoram variáveis XDG.
safe_link "$XDG_CONFIG_HOME" "$HOME/.config"
safe_link "$XDG_DATA_HOME" "$HOME/.local/share"
safe_link "$XDG_CACHE_HOME" "$HOME/.cache"
safe_link "$NPM_CONFIG_CACHE" "$HOME/.npm"
# Classificacao deliberada:
# ~/.config/opencode e' /data, mas node_modules gerado por
# plugins/config pode ir para /cache.
# Se preferir preservar node_modules também, remova este bloco.
if [ ! -e "$XDG_CONFIG_HOME/opencode/node_modules" ]; then
ln -s \
/cache/opencode-config-node_modules \
"$XDG_CONFIG_HOME/opencode/node_modules";
fi;
# Config inicial editavel. Desativa autoupdate dentro do container:
# container deve ser atualizado via rebuild da imagem,
# não mutando binários em runtime.
if [ "${OPENCODE_INIT_CONFIG:-1}" = "1" ]; then
if [ ! -e "$XDG_CONFIG_HOME/opencode/opencode.json" ]; then
(
echo '{';
echo ' "$schema": "https://opencode.ai/config.json",';
echo ' "autoupdate": false';
echo '}';
) > $XDG_CONFIG_HOME/opencode/opencode.json;
fi;
fi;
# Se entrou como root, ajusta somente /data e /cache e
# depois cai para usuário não-root.
# Não faz chown de /workspace para não
# alterar dono dos arquivos do projeto no host.
if [ "$(id -u)" = "0" ]; then
if [ "${OPENCODE_CHOWN:-1}" = "1" ]; then
chown -R opencode:opencode /data /cache
fi
exec gosu opencode "$@"
fi
# Rodar CMD do container
exec "$@";
2.3 – Construção da imagem
Para construir a imagem, crie uma pasta para esse projeto “opencode-builder” crie os dois arquivos (conteúdo acima) nos seguintes caminhos:
- Dockerfile: ./docker/Dockerfile
- Entrypoint: ./rootfs/opt/entrypoint.sh
Execute o build para construir a imagem:
# Constuir imagem opencode:latest
docker build . -f docker/Dockerfile --no-cache -t opencode:latest;
Caso queira construir uma imagem versionada usando uma versão específica do OpenCode:
# Construir imagem de versao especifica do opencode:
OPENCODE_VERSION="1.x.y";
docker build . \
-f docker/Dockerfile\
-t opencode:$OPENCODE_VERSION \
--build-arg OPENCODE_VERSION=$OPENCODE_VERSION;
3 – Rodando OpenCode no Docker
Vamos rodar o OC no Docker, criando o container opencode-dev.
2.1 – Rede Docker
Criando a rede para containers (network_public):
# Rede de containers
docker network create network_public \
-d bridge \
-o com.docker.network.bridge.name=br-net-public \
-o com.docker.network.driver.mtu=1500 \
-o com.docker.network.bridge.gateway_mode_ipv4=nat-unprotected \
--subnet 10.249.0.0/16 \
--gateway 10.249.255.254;
2.2 – Volume
A pasta /workspace dentro do container deve ser mapeada na pasta onde estará seu projeto de software no HOST.
Ela é a pasta onde os programas são executados por padrão (workdir do container).
Nesse exemplo vou armazenar tudo em /storage/opencode-dev para você testar e aprender, e depois você personaliza.
# Diretorio do volume no HOST
DATADIR=/storage/$NAME;
# Pastas de volumes montados na pasta principal do volume no host
mkdir -p $DATADIR;
# - Configs e DB de contexto
mkdir -p $DATADIR/data;
# - Cache do bibliotecas
mkdir -p $DATADIR/cache;
# - Pasta com projeto de software (caminho padrao inicial /workspace)
mkdir -p $DATADIR/workspace;
2.3 – Container do OC
Comando para rodar no “docker run”:
# Variaveis
NAME="opencode-dev";
# Para usar sua imagem local construida no cap. 2
#IMAGE="opencode:latest";
# Para usar minha imagem pronta:
IMAGE="tmsoftbrasil/opencode:latest";
# Diretorio de dados persistentes:
DATADIR=/storage/$NAME;
# Pastas de volumes montados
# na pasta principal do volume no host
mkdir -p $DATADIR;
mkdir -p $DATADIR/data;
mkdir -p $DATADIR/cache;
mkdir -p $DATADIR/workspace;
# Rodar container
# Renovar/rodar
docker rm -f $NAME 2>/dev/null
# --read-only
docker run \
-d --restart=always \
--name $NAME --hostname $LOCAL.intranet.br \
\
--read-only \
--tmpfs /run:rw,noexec,nosuid,size=16m \
--tmpfs /tmp:rw,noexec,nosuid,size=16m \
\
--cpus=2 \
--memory 2g --memory-swap 2g --memory-reservation 256m \
\
--network network_public \
\
-v $DATADIR/data:/data \
-v $DATADIR/cache:/cache \
-v $DATADIR/workspace:/workspace \
\
\
$IMAGE \
tail -f /dev/null;
2.4 – Stack para Compose
Caso prefira no modelo de Stack para docker compose:
name: opencode-dev
services:
opencode-dev:
image: tmsoftbrasil/opencode:latest
container_name: opencode-dev
hostname: opencode-dev
restart: always
read_only: true
command: tail -f /dev/null
tmpfs:
- /run:rw,noexec,nosuid,size=16m
- /tmp:rw,noexec,nosuid,size=16m
deploy:
resources:
limits:
cpus: "2"
memory: 2g
reservations:
memory: 256m
mem_swappiness: 0
memswap_limit: 2g
networks:
- network_public
volumes:
- /storage/opencode-dev/data:/data
- /storage/opencode-dev/cache:/cache
- /storage/opencode-dev/workspace:/workspace
networks:
network_public:
external: true3 – Primeiro acesso
Obtenha shell no container “opencode-dev“:
# Obter shell no container:
docker exec -it opencode-dev bash;
# Rodar opencode:
opencode;
# Ou, rodar direto o opencode dentro do container:
docker exec -it opencode-dev opencode;
3.1 – Tela inicial
Você verá a seguinte tela:

3.2 – Conectando OC a um modelo de IA
O primeiro passo é fornecer um modelo usando o comando “/connect“.
Gratuitamente: Escolha “OpenCode Zen“, acesse https://opencode.ai/zen para obter uma chave, você pode escolher algum modelo “Free” para usar se precisar pagar nada.
Pagos e baratos: Escolha “OpenRouter“, acesse https://openrouter.ai, cadastre-se, coloque algum crédito (15 dólares), recomendo usar o DeepSeek V4 ou algum modelo focado em código com o preço baixo.
Eu uso OpenRouter com DeepSeek V4 PRO. Custa alguns centavos de dolar por hora e produz software com muita precisão.
Após rodar o /connect escolhe OpenRouter:

Inserindo a chave de API gerada no site:

Escolha o modelo:

E por fim, pronto para usar:

Para fechar o OpenCode (não para o container):

3.3 – Criando alguma coisa com IA
Com o OpenCode aberto, basta pedir alguma coisa, o resultado por padrão será gerado em /workspace (dentro do container) que fica na pasta do HOST onde esse volume foi mapeado.
Prompt:

Agente OC trabalho:

Resultado pronto:

Resultado (abri arquivo aviso.html no navegador):

.
Agora é contigo. Use sua criatividade.
“Sábio não é aquele que sabe
de tudo e sim usa tudo que sabe“
Provérbio Chinês
Terminamos por hoje!
Patrick Brandão, patrickbrandao@gmail.com
