Saudações.
Hoje vou ensinar como enlatei o Coder dentro de um container Docker com vários agentes de IA.
Pré-requisitos:
- Servidor, VPS ou VM com Linux;
- Docker;
1 – Sobre o Coder
O Coder é uma IDE de desenvolvimento que funciona no navegador.
Ele é inspirado e compatível com o VSCode.
Depois de um tempo tendo que lidar com gestão de projetos usando o método tradicional de instalar tudo no notebook e as vezes sincronizar com o Git, me veio uma ideia: E se a IDE, o projeto, a compilação, testes, agentes de IA pudessem ficar enlatados no Docker?
Com isso eu poderia programar em qualquer computador, smartphone, tablet, delegar tarefas aos agentes, sem precisar manter nenhum deles ligados.
Links:
- Projeto no GitHub: https://github.com/coder/code-server;
- Site: https://coder.com/
- Documentação: https://coder.com/docs
- Releases: https://github.com/coder/code-server/releases
2 – Guia rápido
Para quem tem pressa!
Crie a rede Docker:
# 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;
Criar volume e configuração inicial com senha tulipa_coder (mude para a sua):
# Diretorio do volume
# Pasta no HOST
mkdir -p /storage/coder-ide;
# Pasta do projeto para mapear em /project do container
mkdir -p /storage/coder-ide/project;
# Raiz do sistema
mkdir -p /storage/coder-ide/.config/code-server;
# Config inicial
#(
# echo;
# echo 'bind-addr: 127.0.0.1:8080';
# echo 'auth: password';
# echo 'password: tulipa_coder';
# echo 'cert: false';
# echo;
#) > /storage/coder-ide/.config/code-server/config.yaml;
# Ajustar permissoes, login 'coder' uid 1000
chown -R 1000:1000 /storage/coder-ide;
Container do Coder:
# Senha de acesso
PASSWORD="tulipa_coder";
# Atualizar imagem
docker pull codercom/code-server:latest;
# Renovar/rodar:
# - Parar atual
docker container stop coder-ide 2>/dev/null;
docker container rm coder-ide 2>/dev/null;
# - Rodar com imagem renovada
docker container run \
-d \
--user root \
--restart always \
--name coder-ide \
--hostname coder-ide.intranet.br \
\
--cpus 2.0 \
--cpu-shares 1024 \
--memory 2g \
--memory-swap 2g \
--memory-reservation 1g \
--shm-size 1g \
\
--network network_public \
--ip 10.249.111.201 \
\
-p 7280:8080 \
\
-v /storage/coder-ide:/root \
-v /storage/coder-ide:/home/coder \
-v /storage/coder-ide/project:/project \
\
-v /var/run/docker.sock:/var/run/docker.sock \
\
-e DEFAULT_WORKSPACE=/project \
-e DOCKER_USER=root \
-e PASSWORD=$PASSWORD \
\
codercom/code-server:latest \
--auth password \
/project;
# entrypoint: /usr/bin/entrypoint.sh
# cmd.......: --bind-addr 0.0.0.0:8080
Versão Docker Compose:
services:
coder-ide:
image: codercom/code-server:latest
container_name: coder-ide
hostname: coder-ide.intranet.br
user: root
restart: always
networks:
network_public:
ipv4_address: 10.249.111.201
ports:
- "7280:8080"
volumes:
- /storage/coder-ide:/root
- /storage/coder-ide:/home/coder
- /storage/coder-ide/project:/project
- /var/run/docker.sock:/var/run/docker.sock
environment:
DEFAULT_WORKSPACE: /project
DOCKER_USER: root
PASSWORD: tulipa_coder
command:
- --auth
- password
- /project
cpus: 2.0
cpu_shares: 1024
mem_limit: 2g
memswap_limit: 2g
mem_reservation: 1g
shm_size: 1g
networks:
network_public:
name: network_public
driver: bridge
driver_opts:
com.docker.network.bridge.name: br-net-public
com.docker.network.driver.mtu: 1500
com.docker.network.bridge.gateway_mode_ipv4: nat-unprotected
ipam:
config:
- subnet: 10.249.0.0/16
gateway: 10.249.255.254
Primeiro acesso: Abra o navegador no endereço do servidor porta HTTP 7280.
x
x
3 – Guia manual
Nesse capítulo vou mostrar como eu fiz para criar um bundle de desenvolvimento no container.
3.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;
3.3 – Containers do Coder
Criando o container do Coder para instalar manualmente os agentes. Mais tarde vamos transformar esses comandos em Dockerfile.
Opcionalmente mapeie o volume para o socket do Docker para que você possa fazer testes em containers durante o desenvolvimento.
# Senha de acesso
PASSWORD="tulipa_coder";
# Diretorio do volume
# Pasta no HOST
mkdir -p /storage/coder-ide;
# Pasta do projeto para mapear em /project do container
mkdir -p /storage/coder-ide/project;
# Raiz do sistema
mkdir -p /storage/coder-ide/.config/code-server;
# Ajustar permissoes, login 'coder' uid 1000
chown -R 1000:1000 /storage/coder-ide;
# Renovar/rodar:
# - Parar atual
docker container stop coder-ide 2>/dev/null;
docker container rm coder-ide 2>/dev/null;
# - Rodar com imagem renovada
docker container run \
-d \
--user root \
--restart always \
--name coder-ide \
--hostname coder-ide.intranet.br \
\
--cpus 2.0 \
--cpu-shares 1024 \
--memory 2g \
--memory-swap 2g \
--memory-reservation 1g \
--shm-size 1g \
\
--network network_public \
--ip 10.249.111.201 \
\
-p 7280:8080 \
\
-v /storage/coder-ide:/root \
-v /storage/coder-ide:/home/coder \
-v /storage/coder-ide/project:/project \
\
-v /var/run/docker.sock:/var/run/docker.sock \
\
-e DEFAULT_WORKSPACE=/project \
-e DOCKER_USER=root \
-e PASSWORD=$PASSWORD \
\
codercom/code-server:latest \
--auth password \
/project;
Vamos preparar ambiente com ferramentas básicas, entre no container:
# Entrar no shell do container:
docker exec -it --user root coder-ide bash;
E instale os pacotes básicos (pacotes presentes em qualquer notebook):
# Atualizar
apt -y update;
apt -y upgrade;
apt -y dist-upgrade;
apt -y full-upgrade;
# Pacotes nativos do Debian, mas que devem estar presentes,
# rode esse comando para garantir que nao faltou nenhum pacote basico:
apt -y install \
bash sudo \
openssl wget curl ca-certificates \
iproute2 htop iputils-ping \
bzip2 tar unzip gzip xz-utils zstd zip \
util-linux coreutils procps psmisc \
less logrotate lsof \
sed grep mawk \
mc nano jq git \
file findutils \
iputils-ping traceroute \
openssh-client;
# SSH
apt -y install rsync;
apt -y install openssh-server;
apt -y install openssh-sftp-server;
# Instalar cliente Docker
apt -y install docker-cli;
apt -y install docker-compose;
apt -y install docker-buildx;
3.4 – Instalando agentes
Essa é a parte boa!
Existem duas formas de fazer isso:
- Agente isolado: Você roda o agente em outro container Docker com acesso ao mesmo diretório do volume do seu projeto (/project), considero isso mais limpo e objetivo. Recomendado para ambientes profissionais. O principal vantagem é não embolar dependências (alguns precisam de node 22, outros de node 24, … vira bagunça).
- Container bundle: Você instala os agentes no mesmo container. A desvantagem é que você precisará gerir bem os volumes usados pelo Coder e pelo Agente para que seu container não destrua configurações e dados ao ser recriado no futuro por ter acumulado dados em pastas dentro do container que não estavam mapeadas fora dele.
Vou fazer o bundle já que rodar isolado é matéria de outro artigo (OpenCode, Pi, …).
Instalando pacotes necessários para rodar os agentes no mesmo container:
# Instalar nodejs e npm
apt -y install nodejs npm;
# Instalar Claude Code
npm install -g @anthropic-ai/claude-code;
# Instalar OpenCode
npm install -g opencode-ai;
# Instalando OpenClaude
npm install -g @gitlawb/openclaude@latest;
# Instalando Agent Pi (bugou comigo)
# npm install -g --ignore-scripts @earendil-works/pi-coding-agent;
Pronto pra usar na porta HTTP 7280.
4 – Criando o container pacotão
Agora vou criar uma única imagem juntando tudo que foi feito no capítulo 3.
Crie e entre na pasta coder-bundle:
mkdir -p /root/coder-bundle;
cd /root/coder-bundle;
Nessa pasta coloque todos os arquivos abaixo:
- supervisord.conf
- sshd_config
- crontab
- entrypoint.sh
- Dockerfile
4.1 – Supervisor
Usamos o supervidord para rodar vários softwares no container. Ele será o PID 1 que manterá e reiniciará todos os softwares caso falhem.
Arquivo supervisord.conf
[unix_http_server]
file = /var/run/supervisor.sock
chmod = 0700
[supervisord]
logfile = /root/log/supervisord.log
pidfile = /run/supervisord.pid
childlogdir = /root/log
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl = unix:///run/supervisor.sock
[program:sshd]
user = root
command = /usr/sbin/sshd -D
priority = 1
directory = /run
autostart = true
autorestart = true
startsecs = 0
stopwaitsecs = 2
stdout_logfile = /root/log/service-%(program_name)s.log
stderr_logfile = /root/log/service-%(program_name)s.err
[program:coder-server]
user = root
command = /usr/bin/entrypoint.sh --bind-addr 0.0.0.0:8080 --auth password /project
priority = 2
directory = /run
autostart = true
autorestart = true
startsecs = 0
stopwaitsecs = 2
stdout_logfile = /root/log/service-%(program_name)s.log
stderr_logfile = /root/log/service-%(program_name)s.err
[program:cron]
user = root
command = /usr/sbin/cron -f -L 8
priority = 3
directory = /run
autostart = true
autorestart = true
startsecs = 0
stopwaitsecs = 2
stdout_logfile = /root/log/service-%(program_name)s.log
stderr_logfile = /root/log/service-%(program_name)s.err
4.2 – Configuração SSH
Para permitir acesso direto no container por SSH temos que salvar as chaves do servidor em volume e autorizar a entrada como root direto.
O container não possui senha no usuário root, você deverá cadastrar sua chave pública no arquivo trusted_keys:
- Dentro do container: /root/ssh/trusted_keys
- No volume: /storage/coder-ide/ssh/trusted_keys
Arquivo sshd_config
VersionAddendum OpenSSH_12
Protocol 2
Port 22
AddressFamily any
ListenAddress 0.0.0.0
ListenAddress ::
IPQoS lowdelay throughput
Compression yes
UseDNS no
ClientAliveInterval 3
ClientAliveCountMax 15
TCPKeepAlive yes
PubkeyAuthentication yes
AuthorizedKeysFile /root/ssh/trusted_keys .ssh/authorized_keys
HostKey /root/ssh/ssh_host_rsa_key
HostKey /root/ssh/ssh_host_ecdsa_key
HostKey /root/ssh/ssh_host_ed25519_key
PasswordAuthentication yes
PermitEmptyPasswords no
PermitListen any
PermitRootLogin yes
KbdInteractiveAuthentication no
UsePAM yes
SyslogFacility AUTH
LogLevel INFO
PubkeyAuthentication yes
RekeyLimit 1G 1h
RequiredRSASize 2048
PrintLastLog no
PrintMotd no
AcceptEnv LANG LC_* COLORTERM NO_COLOR
Subsystem sftp /usr/lib/openssh/sftp-server
AllowAgentForwarding yes
AllowTcpForwarding yes
X11Forwarding yes
X11DisplayOffset 10
GatewayPorts clientspecified
PermitTTY yes
PermitTunnel no
PermitUserEnvironment no
Include /etc/ssh/sshd_config.d/*.conf
4.3 – Crontab
O plano de crontab com intervalos pre-configurados nos permitirá criar e acionar scripts de automação dentro do nosso projeto.
Vou criar em /root/cron/ pois a pasta /root ficará em volume, preservando assim o projeto e os scripts de automação juntos.
Arquivo crontab
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# min hour day month week-day user program args...
0 * * * * root run-parts --regex '.*' /root/cron/hourly
0 2 * * * root run-parts --regex '.*' /root/cron/daily
0 3 * * 6 root run-parts --regex '.*' /root/cron/weekly
0 5 1 * * root run-parts --regex '.*' /root/cron/monthly
*/1 * * * * root run-parts --regex '.*' /root/cron/1min
*/5 * * * * root run-parts --regex '.*' /root/cron/5min
*/10 * * * * root run-parts --regex '.*' /root/cron/10min
*/15 * * * * root run-parts --regex '.*' /root/cron/15min
*/30 * * * * root run-parts --regex '.*' /root/cron/30min
0 0 * * 0 root run-parts --regex '.*' /root/cron/sunday
0 0 * * 1 root run-parts --regex '.*' /root/cron/monday
0 0 * * 2 root run-parts --regex '.*' /root/cron/tuesday
0 0 * * 3 root run-parts --regex '.*' /root/cron/wednesday
0 0 * * 4 root run-parts --regex '.*' /root/cron/thursday
0 0 * * 5 root run-parts --regex '.*' /root/cron/friday
0 0 * * 6 root run-parts --regex '.*' /root/cron.saturday
4.4 – Entrypoint
Vou criar um novo entrypoint com supervidord para rodar o Coder Server e o OpenSSH Server, dessa forma poderemos entrar via SSH direto no container.
Arquivo entrypoint.sh
#!/bin/sh
# Garantir existencia dos diretorios
mkdir -p /root/log;
mkdir -p /run;
mkdir -p /project;
# Preparativos do supervisord
touch /root/log/supervisord.log;
# Preparativos do servidor SSH
# Pasta de configs e chaves do servidor
mkdir -p /root/ssh;
# Pasta de configs e chaves do usuario
mkdir -p /root/.ssh;
# Arquivo de cadastro de cahves conviaveis dos clientes
touch /root/ssh/trusted_keys;
touch /root/.ssh/authorized_keys;
# Chaves do servidor SSH
[ -s /root/ssh/ssh_host_rsa_key ] || {
ssh-keygen -t rsa -b 4096 -f /root/ssh/ssh_host_rsa_key -N "";
chmod 600 /root/ssh/ssh_host_rsa_key;
};
[ -s /root/ssh/ssh_host_ecdsa_key ] || {
ssh-keygen -t ecdsa -b 521 -f /root/ssh/ssh_host_ecdsa_key -N "";
chmod 600 /root/ssh/ssh_host_ecdsa_key;
};
[ -s /root/ssh/ssh_host_ed25519_key ] || {
ssh-keygen -t ed25519 -f /root/ssh/ssh_host_ed25519_key -N "";
chmod 600 /root/ssh/ssh_host_ed25519_key;
};
# Chave de usuario local pre-provisionada
[ -s /root/.ssh/id_ed25519 ] || {
ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N "";
chmod 600 /root/.ssh/id_ed25519;
};
# Preparativos do crontab
crondirs="
1min 10min 15min 30min
hourly daily weekly monthly
sunday monday tuesday wednesday
thursday friday saturday
";
for cdir in $crondirs; do
mkdir -p /root/cron/$cdir;
done;
# Comando
EXEC_CMD="$@"
# Rodar CMD
if [ "x$EXEC_CMD" = "x" ]; then
FULLCMD="exec sleep 252288000";
else
FULLCMD="exec $EXEC_CMD";
fi;
echo "Executando: $FULLCMD";
eval $FULLCMD;
4.5 – Dockerfile
Construtor da imagem.
Arquivo Dockerfile
# syntax=docker/dockerfile:1.7
# Coder Server pronto, continuando
FROM codercom/code-server:latest
USER root
ENV \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8
# Instalar pacotes basicos
RUN set -eux; \
apt -y update; \
apt -y upgrade; \
apt -y dist-upgrade; \
apt -y full-upgrade; \
\
apt -y install \
bash sudo \
openssl wget curl ca-certificates \
iproute2 htop iputils-ping \
bzip2 tar unzip gzip xz-utils zstd zip \
util-linux coreutils procps psmisc \
less logrotate lsof \
sed grep mawk \
mc nano jq git \
file findutils \
iputils-ping traceroute \
supervisor
# Instalar cliente Docker
RUN apt -y install docker-cli \
docker-compose \
docker-buildx
# Instalar servidor SSH para acesso remoto
RUN apt -y install \
rsync \
openssh-server \
openssh-sftp-server \
openssh-client
# Instalar nodejs
RUN apt -y install \
nodejs \
npm
# Instalar claude-code
RUN npm install -g @anthropic-ai/claude-code
# Instalar opencode
RUN npm install -g opencode-ai
# Instalar openclaude
RUN npm install -g @gitlawb/openclaude@latest
# Configs personalizadas
COPY supervisord.conf /etc/supervisord.conf
COPY sshd_config /etc/ssh/sshd_config
COPY crontab /etc/crontab
# Script de entrypoint
COPY entrypoint.sh /opt/entrypoint.sh
# Finalizacao e arremate
RUN chmod +x /opt/entrypoint.sh
# Diretorio padrao inicial
WORKDIR /project
# Entrypoint para supervidord
# /opt/entrypoint.sh /usr/bin/supervisord -n -c /etc/supervisord.conf
ENTRYPOINT ["/opt/entrypoint.sh"]
CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf"]
4.6 – Construção da imagem
Execute o build para construir a imagem:
# Constuir imagem:
docker build . \
-f Dockerfile \
-t coder-bundle:latest;
4.7 – Rodando pacotão
Agora basta usar a imagem coder-bundle:latest para ter um super ambiente de desenvolvimento isolado, na nuvem, cheio de bots trabalhadores.
# Senha de acesso
PASSWORD="tulipa_coder";
# Diretorio do volume
# Pasta no HOST
mkdir -p /storage/coder-ide;
# Pasta do projeto para mapear em /project do container
mkdir -p /storage/coder-ide/project;
# Raiz do sistema
mkdir -p /storage/coder-ide/.config/code-server;
# Ajustar permissoes, login 'coder' uid 1000
chown -R 1000:1000 /storage/coder-ide;
# Renovar/rodar:
# - Parar atual
docker container stop coder-ide 2>/dev/null;
docker container rm coder-ide 2>/dev/null;
# - Rodar com imagem renovada
docker container run \
-d \
--user root \
--restart always \
--name coder-ide \
--hostname coder-ide.intranet.br \
\
--cpus 2.0 \
--cpu-shares 1024 \
--memory 2g \
--memory-swap 2g \
--memory-reservation 1g \
--shm-size 1g \
\
--network network_public \
--ip 10.249.111.201 \
\
-p 7422:22 \
-p 7480:8080 \
\
-v /storage/coder-ide:/root \
-v /storage/coder-ide:/home/coder \
-v /storage/coder-ide/project:/project \
\
-v /var/run/docker.sock:/var/run/docker.sock \
\
-e DEFAULT_WORKSPACE=/project \
-e DOCKER_USER=root \
-e PASSWORD=$PASSWORD \
\
coder-bundle:latest;
O acesso do container acima está na porta HTTP 7422, o acesso pode ser feito por SSH, porta externa SSH 7422.
Terminamos por hoje!
Patrick Brandão, patrickbrandao@gmail.com
“Uma só mão não aplaude“
Provérbio Árabe
