Referencia config.yml (Global)
O arquivo /opt/runner/config.yml define a configuracao global do Runner no servidor.
Estrutura Completa
# ============================================
# CAMINHOS
# ============================================
apps_path: /data/apps # Diretorio raiz das apps
traefik_dynamic_path: /etc/traefik/dynamic # Configs dinamicas Traefik
state_path: /opt/runner/state # Estado do Runner
logs_path: /opt/runner/logs # Logs do Runner
# ============================================
# DOCKER
# ============================================
registry: registry.local # Registry Docker (opcional)
keep_versions: 3 # Versoes a manter por padrao
max_deploy_retries: 3 # Max tentativas para mesmo commit (0 = ilimitado)
# ============================================
# TRAEFIK
# ============================================
traefik:
mode: external # local | external | none (v2.20.0+)
entrypoints:
- websecure # Entrypoint HTTPS
cert_resolver: myresolver # Resolver de certificados
external: # v2.21.0+ sub-block (só usado quando mode=external)
write_snippet_to: "/opt/traefik-snippets/{app}.yml" # opcional, salva em arquivo
print_snippet: true # default true, emite snippet em stderr
# ============================================
# GITHUB
# ============================================
github:
default_user: devborlot # Usuario GitHub padrao
# ============================================
# NOTIFICACOES (v1.4.0+)
# ============================================
notify:
channel: telegram # telegram | discord | both
telegram_bot_token: "123456:ABC-DEF..." # Token do bot (@BotFather)
telegram_chat_id: "-1001234567890" # ID do chat/grupo/canal
discord_webhook: "" # URL do webhook Discord
verbose_errors: true # Segunda mensagem com erro completo (v2.0.7)
# ============================================
# PR AUTO-CLEANUP (v2.0.0)
# ============================================
auto_cleanup_prs: true # Remove PRs expirados (TTL) no fetch
# ============================================
# LOGS ESTRUTURADOS v1.2.0
# ============================================
logs:
deploy_events:
type: structured_log
path: /opt/runner/logs/deploy-events.jsonl
format: json_lines
# ============================================
# TRIGGERS v1.2.0
# ============================================
triggers:
mode: auto # auto | webhook | cron | both
webhook:
enabled: true
port: 8080
path: /webhook
secret: "${WEBHOOK_SECRET}" # Para verificacao HMAC
cron:
enabled: true
canary_tick_interval: 60 # Segundos entre ticks do scheduler
# ============================================
# STAGING
# ============================================
staging:
enabled: true # Habilitar staging de PRs
base_path: /opt/runner/staging # Diretorio base
network: public # Network Docker
default_ttl_hours: 48 # TTL padrao
max_total_instances: 20 # Max stagings total
# ============================================
# NETWORK / PORT ALLOCATION (v2.19.0)
# ============================================
network:
vpc_ip: 10.108.0.5 # opcional. Sem isso → bind 127.0.0.1 (loopback)
publish_strategy: random # random | sequential | explicit | none
port_range: [8000, 8999] # range pra alocacao automatica
port_blocklist: [8080, 9000] # opcional — portas a ignorarCampos Detalhados
Caminhos
| Campo | Tipo | Default | Descricao |
|---|---|---|---|
apps_path |
string | /data/apps |
Diretorio raiz das aplicacoes |
traefik_dynamic_path |
string | /etc/traefik/dynamic |
Configs dinamicas do Traefik |
state_path |
string | /opt/runner/state |
Diretorio de estado |
logs_path |
string | /opt/runner/logs |
Diretorio de logs |
Docker
| Campo | Tipo | Default | Descricao |
|---|---|---|---|
registry |
string | - | URL do registry Docker privado |
keep_versions |
integer | 3 |
Quantas versoes manter por padrao |
max_deploy_retries |
integer | 3 |
Max tentativas de deploy para o mesmo commit que falhou. 0 = ilimitado (v1.4.1+) |
docker.auto_prune_dangling |
boolean | true |
v2.23.20+. Apos deploy build:, roda docker image prune -f (so untagged sem container). Safe. |
docker.auto_prune_build_cache |
string | "72h" |
v2.23.20+. Apos deploy build:, prune do buildkit cache mais velho que isso. Aceita 72h, 7d, all, ou "" (desabilita). |
Traefik
| Campo | Tipo | Default | Descricao |
|---|---|---|---|
traefik.mode |
string | local |
Modo de roteamento. Detalhe abaixo. |
traefik.entrypoints |
array | [websecure] |
Entrypoints a usar |
traefik.cert_resolver |
string | myresolver |
Resolver de certificados |
traefik.external.write_snippet_to |
string | - | Path opcional pra salvar snippet (Ansible sync). {app} placeholder expande pro project slug (v2.21.0+) |
traefik.external.print_snippet |
boolean | true |
Imprime snippet em stderr depois do deploy (v2.21.0+) |
`traefik.mode` — quatro modos
| Valor | Quando usar | Como funciona |
|---|---|---|
local (default) |
Traefik na mesma docker network dos containers (bridge public ou similar) |
Runner escreve <traefik_dynamic_dir>/<app>.yml com url: http://<container_name>:port. Traefik resolve via Docker DNS interno. |
docker_labels (v2.23.11+) |
Traefik em network_mode: host OU quando você quer routing reativo a start/stop |
Runner emite labels Traefik no container. Traefik com provider docker (socket mount) descobre o container e seu bridge IP automaticamente. Requer Traefik configurado com providers.docker.endpoint: unix:///var/run/docker.sock e os containers conectados à network indicada em traefik.network. Limitação: canary não suportado (weighted service não cabe em labels per-container). |
external (v2.20.0+) |
Traefik em outro host (sync via Ansible/scp) | Skipa write local + emite snippet copy-pasteable em stderr (e/ou em external.write_snippet_to). |
none (v2.20.0+) |
App interna sem HTTP público | Nenhuma config Traefik gerada. Container sobe mas não tem rota. |
Erro comum: Traefik em network_mode: host + traefik.mode: local → dynamic file escrito com container name → Traefik no host não resolve via Docker DNS → dial tcp: lookup <container> on 127.0.0.53:53: server misbehaving → service marked DOWN → 503 pro cliente. Switch pra docker_labels resolve.
Output em mode=external (v2.21.0+):
✅ DEPLOY CONCLUÍDO
Endpoint: http://10.0.0.5:8473
📋 SNIPPET TRAEFIK (cole no <traefik-server>:/etc/traefik/dynamic/<file>.yml):
http:
routers:
meu-app-production:
rule: "Host(`app.example.com`)"
entryPoints: ["websecure"]
tls: { certResolver: myresolver }
service: meu-app-production
services:
meu-app-production:
loadBalancer:
servers:
- url: "http://127.0.0.1:8473"
ℹ️ Snippet salvo em /opt/traefik-snippets/meu-app.ymlCom print_snippet: true (default), o snippet vai em stderr depois do deploy. Com write_snippet_to: configurado, também salva em arquivo (Ansible sync). Os dois podem coexistir.
GitHub
| Campo | Tipo | Default | Descricao |
|---|---|---|---|
github.default_user |
string | - | Usuario GitHub padrao |
Notificacoes (v1.4.0+)
| Campo | Tipo | Default | Descricao |
|---|---|---|---|
notify.channel |
string | telegram |
Canal: telegram, discord ou both |
notify.telegram_bot_token |
string | - | Token do bot Telegram (@BotFather) |
notify.telegram_chat_id |
string | - | ID do chat/grupo/canal (negativo para grupos) |
notify.discord_webhook |
string | - | URL completa do webhook Discord |
notify.verbose_errors |
boolean | true |
Envia segunda mensagem com erro completo quando deploy falha (v2.0.7) |
PR Auto-Cleanup (v2.0.0)
| Campo | Tipo | Default | Descricao |
|---|---|---|---|
auto_cleanup_prs |
boolean | true |
Remove automaticamente PRs expirados (TTL) durante o fetch. PRs com branch deletada sao marcados como stale |
Setup automatico:
runner notify setup --channel telegram # Setup interativo Telegram
runner notify setup --channel discord # Setup interativo Discord
runner notify setup --channel both # Setup ambos
runner notify test # Testar envio
runner notify status # Ver status dos canaisO setup Telegram auto-detecta o chat_id de DMs, grupos e canais. Para canais, o bot precisa ser admin.
Canais ativados: O NotifyManager v2 ativa canais automaticamente com base nos campos preenchidos. Alem disso, um StructuredLogChannel grava todos os eventos em deploy-events.jsonl no diretorio de logs.
Logs (v1.2.0)
Configuracao de logs estruturados.
logs:
deploy_events:
type: structured_log
path: /opt/runner/logs/deploy-events.jsonl
format: json_linesFormatos:
json_lines- Um JSON por linha (JSONL)ndjson- Newline Delimited JSON (equivalente)
Exemplo de saida:
{"timestamp":"2026-02-16T14:30:00Z","event":"deploy_success","project":"meu-app","version":"v1.2.0","instance":"production","duration_ms":45000}
{"timestamp":"2026-02-16T14:35:00Z","event":"canary_weight_changed","project":"meu-app","version":"v1.2.1","instance":"production","weight":30}Triggers (v1.2.0)
Configuracao de triggers automaticos.
triggers:
mode: auto
webhook:
enabled: true
port: 8080
path: /webhook
secret: "${WEBHOOK_SECRET}"
cron:
enabled: true
canary_tick_interval: 60Modos:
| Modo | Comportamento |
|---|---|
auto |
Webhook se secret configurado, senao cron |
webhook |
Apenas webhook server |
cron |
Apenas cron scheduler |
both |
Webhook e cron simultaneamente |
Webhook:
| Campo | Tipo | Default | Descricao |
|---|---|---|---|
enabled |
boolean | true |
Habilitar webhook server |
port |
integer | 8080 |
Porta do servidor |
path |
string | /webhook |
Path do endpoint |
secret |
string | - | Secret para verificacao HMAC |
Cron:
| Campo | Tipo | Default | Descricao |
|---|---|---|---|
enabled |
boolean | true |
Habilitar cron scheduler |
canary_tick_interval |
integer | 60 |
Segundos entre ticks do canary scheduler |
Staging
| Campo | Tipo | Default | Descricao |
|---|---|---|---|
staging.enabled |
boolean | false |
Habilitar staging de PRs |
staging.base_path |
string | /opt/runner/staging |
Diretorio base |
staging.network |
string | public |
Network Docker |
staging.default_ttl_hours |
integer | 48 |
TTL padrao em horas |
staging.max_total_instances |
integer | 20 |
Max stagings global |
Network / Port Allocation (v2.19.0)
Bloco opcional. Sem ele, o runner usa publish_strategy=Explicit por default — comportamento idêntico à v2.18.x (lê ports: do .deploy.yml).
network:
vpc_ip: 10.108.0.5
publish_strategy: random
port_range: [8000, 8999]
port_blocklist: [8080]| Campo | Tipo | Default | Descricao |
|---|---|---|---|
network.vpc_ip |
string (IPv4) | - | IP da interface VPC. Sem isso, bind cai pra 127.0.0.1 (Traefik no mesmo host alcança via loopback) |
network.publish_strategy |
string | explicit |
Ver tabela abaixo |
network.port_range |
[int, int] |
[8000, 8999] |
Range fechado pra alocação automática |
network.port_blocklist |
array de int | [] |
Portas a ignorar mesmo dentro do range |
Strategies disponíveis:
| Strategy | Comportamento |
|---|---|
random |
Sorteia porta livre na primeira deploy. Persiste sticky em state.network.published_port — re-deploys reusam. |
sequential |
Pega primeira porta livre no range (em ordem crescente). Sticky igual random. |
explicit (default) |
Lê ports: do .deploy.yml (comportamento v2.18.x). Ignora port_range. |
none |
Container roda sem -p no host (só na rede docker). Útil pra workers/sidecars. |
Sticky lifecycle:
- Primeira deploy: aloca porta → persiste em
state/<app>.yml::network.published_port - Re-deploys (commit novo,
runner fetch --deploy): reusa a mesma porta — Traefik externo permanece válido runner unregister X: libera porta de volta ao poolrunner reset --hard X: libera state → próximo deploy realocarunner ports realloc X: força nova alocação (operador atualiza Traefik externo)
Backwards compat:
- Sem bloco
network:no config →publish_strategy = Explicit→ comportamento idêntico v2.18.x - Apps registradas em v2.18.x continuam rodando sem precisar migrar
- State files antigos sem bloco
networkmigram silenciosamente — porta re-alocada na próxima deploy quando strategy != Explicit
Setup interativo:
runner init
# → "Esse server fica em uma VPC interna? [s/N]"
# → "IP detectado: 10.108.0.5 (interface eth1). Confirma? [Y/n]"
# → "Strategy [random/sequential/explicit] (default: random)"Em modo não-TTY (CI, piped) skipa silenciosamente — backcompat 100%.
Veja também:
- `runner ports` — list / release / realloc
- `runner deploy --port / --no-port` — override one-shot
- `runner status --json` — campo
runtime.network
Auto-pause (v2.23.19)
Apps que falham git ls-remote (ou similar) repetidas vezes consecutivas viram paused automaticamente e o cron para de tentar.
max_consecutive_fetch_errors: 10 # default; 0 = desabilita| Campo | Tipo | Default | Descricao |
|---|---|---|---|
max_consecutive_fetch_errors |
integer | 10 |
Quantos erros consecutivos no fetch antes de marcar a app como paused. 0 desabilita o auto-pause. |
Quando vira paused:
state.consecutive_fetch_errors >= max_consecutive_fetch_errorsstate.statusmuda prapaused- Cron skipa a app silenciosamente no proximo tick
- Resultado: para de poluir o log (caso
printcom repo deletado)
Como reativar:
runner unpause <app> # clear de paused + reset do counter
runner unpause <app> --jsonrunner reset <app> tambem reativa (clear total do state). Para casos onde a unica coisa errada eh o paused, prefira unpause.
Env Sync (v2.23.18)
Controla se .env/.secrets sao regenerados a partir do secrets.enc remoto quando o hash muda durante deploy.
env_sync_default: remote # default — remoto soberano
env_sync_default: skip # local soberano (staging servers)| Campo | Tipo | Default | Descricao |
|---|---|---|---|
env_sync_default |
string | remote |
Default global para env_sync. Apps sem env_sync: explicito no .deploy.yml herdam este valor. |
Valores:
| Valor | Comportamento |
|---|---|
remote |
Se secrets.enc do repo mudou (hash diferente), decripta e sobrescreve .env/.secrets locais |
skip |
.env/.secrets locais nunca sao sobrescritos por mudancas no secrets.enc remoto |
Caso de uso principal: servidores de staging/teste onde o operador ajusta .env manualmente e nao quer que um deploy sobrescreva com valores de outro servidor.
Precedencia:
- Per-app
env_sync:no.deploy.yml(seskip, sempre vence) - Global
env_sync_defaultnoconfig.yml - Se ambos omitidos:
remote(retrocompativel)
Nota: .deploy.yml sempre sincroniza do remoto independente do env_sync — ele controla o contrato do deploy.
Exemplo Minimo
apps_path: /data/apps
traefik_dynamic_path: /data/assets/traefik/dynamic
state_path: /opt/runner/state
logs_path: /opt/runner/logs
traefik:
entrypoints:
- websecure
cert_resolver: myresolver
github:
default_user: meu-usuarioExemplo Completo (v2.4.1)
apps_path: /data/apps
traefik_dynamic_path: /data/assets/traefik/dynamic
state_path: /opt/runner/state
logs_path: /opt/runner/logs
registry: registry.meuservidor.com.br
keep_versions: 5
max_deploy_retries: 3
traefik:
entrypoints:
- websecure
- web
cert_resolver: myresolver
github:
default_user: devborlot
# Notificacoes v1.4.0
notify:
channel: both
telegram_bot_token: "123456789:ABCdefGHI..."
telegram_chat_id: "-1001234567890"
discord_webhook: "https://discord.com/api/webhooks/123456/abcdef..."
# Triggers
triggers:
mode: auto
webhook:
enabled: true
port: 8080
path: /webhook
secret: "${GITHUB_WEBHOOK_SECRET}"
cron:
enabled: true
canary_tick_interval: 60
# Staging
staging:
enabled: true
base_path: /opt/runner/staging
network: public
default_ttl_hours: 72
max_total_instances: 30Inicializacao
O arquivo e criado automaticamente com:
runner initPara ver o template sem criar:
runner init --show-templatePara sobrescrever existente:
runner init --forceDiretorios Criados
Apos runner init, os seguintes diretorios sao criados:
/opt/runner/
├── runner # Binario
├── config.yml # Este arquivo
├── state/ # Estado das apps
│ ├── {app}.yml # Estado por app
│ └── canary/ # Estado do canary scheduler
│ └── {project}_{instance}.yml
└── logs/ # Logs
├── fetch-audit.json # Log de fetches
└── deploy-events.jsonl # Eventos de deploy (v1.2.0)
/data/apps/ # Raiz das aplicacoes
└── {projeto}_{instance}/ # Cada app/instanciaPermissoes
# Permissoes recomendadas
chown -R root:docker /opt/runner/
chmod 750 /opt/runner/
chmod 640 /opt/runner/config.yml
chmod 750 /opt/runner/state/
chmod 750 /opt/runner/logs/Variaveis de Ambiente
O config.yml suporta interpolacao de variaveis de ambiente:
integrations:
telegram:
url: "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage"As variaveis sao resolvidas em tempo de execucao.
Variaveis recomendadas:
TELEGRAM_BOT_TOKEN- Token do bot TelegramDISCORD_WEBHOOK_URL- URL completa do webhook DiscordFLARE_API_TOKEN- Token da API FlareSLACK_WEBHOOK_URL- URL do webhook SlackGITHUB_WEBHOOK_SECRET- Secret para webhooks GitHubCCS_CLIENT_ID/CCS_CLIENT_SECRET- Credenciais OAuth2 CCS