Anatomia do `.deploy.yml`
O .deploy.yml é o único arquivo de configuração que o Runner lê para deployar sua app. Vive no repositório da app, junto com o código-fonte. Esta página percorre as seções do manifest de cima para baixo. Para o schema exato com todos os campos e valores válidos, veja a Referência do .deploy.yml.
Pré-requisitos
- Runner instalado e inicializado (
runner init) - App registrada via
runner addourunner wizard - Repo da app clonado (o
.deploy.ymlvive na raiz do repo)
Seções top-down
project
Identificador único da app. Aparece em logs, notificações, nomes de container e arquivos de state. Deve ser imutável depois do primeiro deploy — mudar exige re-registrar a app.
project: minha-apiproject_group (opcional, v2.43.2+)
Rótulo guarda-chuva (cliente/produto) exibido como Projeto: na notificação de
deploy. Não confundir com project (nome registrado do app, exibido como
Deploy:). Puramente cosmético — não afeta build, routing nem state. Ausente →
a linha Projeto: não aparece.
project: sweepapex-useradmin-front
project_group: Sweepapexsystem
Campo cosmético. Identifica o subtipo da app (front, sys, api, worker). Usado no display de logs e no atalho CLI runner deploy projeto/sistema. Não afeta build nem routing.
project: minha-api
system: sysimage ou build
Define como o container é criado. Os dois modos são mutuamente exclusivos.
Modo IMAGE — usa imagem pronta de registry. O Runner monta o código via volume.
image: caddy:2-alpine
port: 80Modo BUILD — faz docker build usando o Dockerfile do repo. O código fica embutido na imagem.
build:
dockerfile: Dockerfile
context: .
port: 8000| Modo IMAGE | Modo BUILD | |
|---|---|---|
Tem image: |
Sim | Não |
Tem build: |
Não | Sim |
| Faz build? | Não | Sim (docker build) |
| Código | Volume mount | Embutido na imagem |
| Dockerfile no repo | Não precisa | Obrigatório |
Para entender como o wizard escolhe entre os dois modos ao detectar o repo, veja Wizard state machine.
port
Porta interna que o container expõe. O Runner aloca a porta externa automaticamente e mantém ela fixa por app entre deploys (sticky). Override via runner deploy --port.
port: 8000networks
Redes Docker para conectar o container. A rede public é obrigatória para apps que usam Traefik (qualquer app com domain:).
networks:
- public
- mysql
- redishealthcheck
Define como o Runner valida que a app subiu com sucesso. Sem healthcheck válido, o Traefik não roteia — a app fica fora do ar do ponto de vista do usuário.
Cinco modos disponíveis:
| Campo | Modo | Quando usar |
|---|---|---|
path: /health |
HTTP (default) | Apps web que expõem endpoint |
tcp: 3306 |
TCP | Serviços sem HTTP (MySQL, Redis) |
cmd: ["/bin/check"] |
CMD | Imagem que já tem binário de check |
mode: exit |
Exit | CLI tools/scanners sem container permanente |
mode: skip_check |
Skip | Escape hatch explícito (emite aviso) |
healthcheck:
path: /health
interval: 30s
timeout: 10s
retries: 3Para detalhes de cada modo, veja Healthcheck.
instances
Cada instância define um ambiente independente com seu próprio domínio e state. Mínimo de uma instância; sem máximo.
instances:
production:
domain: app.projeto.com.br
source:
type: dist
keep_versions: 3
staging:
domain: staging.projeto.com.br
source:
type: branch
branch: dev
keep_versions: 2source.type define de onde vem o código: dist (branch de artefatos), branch, tag, local ou image.
Domain com placeholder (v2.30.13+): o campo domain aceita a mesma sintaxe {{::Label?default}} dos secrets — útil pra reusar o mesmo .deploy.yml em servidores com domínios diferentes (ex: domain: "{{::AppDomain?app.exemplo.com}}"). O Runner resolve o template em runtime contra .env e .secrets da app. Se o placeholder não resolve e não tem default, o deploy aborta com erro actionable em vez de gerar um router Traefik literal que nunca matcha.
environment e secrets
Variáveis injetadas no container. A diferença entre os dois campos é onde o valor vai parar e se é encriptado.
| Campo | Arquivo destino | Encriptado no repo? | Para que usar |
|---|---|---|---|
environment: |
.env |
Não | Config não-sensível |
secrets: |
.secrets |
Sim (.runner/secrets.enc) |
Senhas, tokens, API keys |
environment:
FLASK_ENV: production
LOG_LEVEL: info
secrets:
DB_PASSWORD: "{RANDOM}"
JWT_SECRET: "${GENERATE:hex:64}"
SMTP_PASSWORD: "{{::SMTP Password}}"Os valores suportam sintaxe especial: literais, prompts interativos ({{::Label?default}}), e geradores automáticos ({RANDOM}, ${GENERATE:hex:N}). Para o ciclo de vida completo de encriptação e recuperação, veja Secrets lifecycle.
required (v2.32.0+)
Lista flat de nomes de variáveis que devem resolver para valor não-vazio em .env ou .secrets antes do runner deploy proceder. Plaintext vazio (KEY=) conta como missing — fecha o cenário de Next.js crashando no startup com NEXT_PUBLIC_X="".
required:
- NEXT_PUBLIC_SUPABASE_ANON_KEY
- DB_PASSWORDSe alguma estiver ausente ou vazia, runner deploy bailha fatal antes de tocar Docker. Bypass de emergência: --allow-missing-required.
Use runner env validate <app> (sem --required) como pre-deploy check no CI — cai no required: do manifest automaticamente.
build.args → environment (auto-propagation, v2.32.0+)
Apps frontend não precisam mais declarar NEXT_PUBLIC_* duas vezes. Quando build.args: tem uma key cujo nome bate com prefixo público conhecido, ela aparece automaticamente em runtime (environment efetivo do container):
| Prefixo | Framework |
|---|---|
NEXT_PUBLIC_ |
Next.js |
VITE_ |
Vite (Vue / React-via-Vite / Svelte) |
REACT_APP_ |
Create React App |
NUXT_PUBLIC_ |
Nuxt 3 runtimeConfig.public |
EXPO_PUBLIC_ |
Expo / React Native (web) |
PUBLIC_ |
SvelteKit |
build:
args:
NEXT_PUBLIC_API_URL: "{{::API_URL}}" # propaga pra env automaticamente
VITE_FEATURE_X: "on" # propaga
NPM_TOKEN: "{{::NPM_TOKEN}}" # NÃO propaga (não bate prefix)
environment:
NODE_ENV: production
# NEXT_PUBLIC_API_URL não precisa estar aqui — já vem de build.argsenvironment: explícito sempre ganha — se você declarar a key em ambos, o valor de environment: é usado.
Tokens privados (NPM_TOKEN, GH_TOKEN, DATABASE_URL) ficam confinados ao build step, preservando o boundary de build-time secret.
version
Fonte de onde o Runner extrai o número de versão para exibir em logs e notificações.
version:
source: file
file: package.json
field: versionFontes disponíveis: file (lê campo de JSON), git-tag (tag mais recente), git-commit (hash).
notify
Lista de subscrições de notificação por app, adicional ao notify: global do Runner. Cada item referencia um canal nomeado definido no config.yml global — o .deploy.yml nunca carrega credenciais.
notify:
- on: [deploy_success, deploy_failure]
when: "{{::instance_name}} == production"
channel: ops_email
subject: "[{{::repository_name}}] {{::deploy_result_status}}"
template: "{{::repository_name}} v{{::deploy_version}} — {{::deploy_result_status}}"
- on: [deploy_rollback]
channel: meu_webhook
request:
method: POST
url: "https://hooks.example.com/{{::TOKEN}}"
headers:
Authorization: "Bearer {{::TOKEN}}"
body: '{"msg":"{{::repository_name}} rollback"}'| Campo | Obrigatório | Descrição |
|---|---|---|
on: |
Sim | Eventos que disparam a subscrição: deploy_started, deploy_success, deploy_failure, deploy_rollback |
when: |
Não | Condição simples {{::var}} == valor ou != valor. Sem when, dispara sempre |
channel: |
Sim | Nome de um canal declarado em notify.channels: no config.yml global |
template: |
Para canais telegram/smtp |
Corpo da mensagem |
subject: |
Opcional (smtp) |
Assunto do e-mail |
request: |
Para canais webhook |
method, url, headers, body — todos aceitam {{::var}} |
A sintaxe {{::var}} é a mesma usada em secrets:. Além do contexto do deploy (repository_name, deploy_result_status, deploy_version, deploy_error, instance_name, instance_domain, event, deploy_id, project), também é possível referenciar variáveis do próprio environment:/secrets: do app, resolvidas do bundle criptografado no momento do deploy.
Uma subscrição com canal ausente, mal configurado, ou que falhe no envio é apenas logada (fire-and-forget) — nunca bloqueia o deploy.
Os canais nomeados (telegram, webhook, smtp) são configurados no config.yml global do Runner. Ver guides/operar/notificacoes.md.
Campos avançados
Além dos campos acima, o .deploy.yml suporta:
| Campo | Para que serve | Desde |
|---|---|---|
volumes: |
Bind mounts e volumes nomeados | v1 |
ports: |
Mapeamento de portas sem Traefik | v2.0.1 |
depends_on: |
Ordem de deploy em runner deploy --all |
v1 |
required: |
Variáveis obrigatórias (pre-flight bail) | v2.32.0 |
resources: |
Limites de CPU/memória | v2.24.0 |
security: |
Hardening do container | v2.24.0 |
logging: |
Driver e rotação de logs | v2.24.0 |
network_aliases: |
DNS alias estável entre containers | v2.24.0 |
command: |
Override do CMD da imagem | v2.26.3 |
env_sync: |
Soberania de .env/.secrets |
v2.23.18 |
routing_mode: |
Obrigatório para canary deployment | v1.2.0 |
Para todos os campos e tipos exatos, veja a Referência do .deploy.yml.
Manifest mínimo funcional
project: minha-app
image: caddy:2-alpine
port: 80
instances:
production:
domain: app.meusite.com.br
source:
type: distO que pode dar errado
| Erro | Causa provável | Como resolver |
|---|---|---|
| Campo desconhecido | Typo ou campo depreciado | runner validate minha-app aponta a linha |
image e build juntos |
Modos mutuamente exclusivos | Manter apenas um dos dois |
Container unhealthy |
Healthcheck mal configurado ou endpoint inexistente | Ver Healthcheck |
| Drift detectado | docker-compose.yml divergiu do manifest gerado |
Ver Migrate compose |
| Secrets não restaurados | Bundle .runner/secrets.enc ausente ou ckey errada |
Ver Secrets lifecycle |