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 add ou runner wizard
  • Repo da app clonado (o .deploy.yml vive 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-api

project_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: Sweepapex

system

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: sys

image 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: 80

Modo 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: 8000

networks

Redes Docker para conectar o container. A rede public é obrigatória para apps que usam Traefik (qualquer app com domain:).

networks:
  - public
  - mysql
  - redis

healthcheck

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: 3

Para 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: 2

source.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_PASSWORD

Se 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.args

environment: 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: version

Fontes 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: dist

O 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
By Borlot.com.br on 27/05/2026