runner deploy
Executa o pipeline de deploy completo para uma aplicacao ou todas as apps registradas.
Sintaxe
runner deploy <app>
runner deploy <app> [--version <version>]
runner deploy --allParametros
| Parametro | Tipo | Obrigatorio | Descricao |
|---|---|---|---|
<app> |
string | Sim* | Caminho ou nome do projeto (ex: meu-app ou /data/apps/meu-app) |
--version, -v |
string | Nao | Versao especifica para deploy |
--image-tag <tag> |
string | Nao | (v2.173.0+) App com source registry: tag da imagem a implantar. O runner resolve a tag em digest antes do deploy, e a tag vira a versao. Recusado em app que nao e registry (image_tag_sem_registry). Nao combina com --all, --version nem --local. Ver Deploy por imagem |
--instance, -i <nome> |
string | Nao | (v2.174.0+) Confere que a instancia pedida e a do registro da app. Se for outra, o deploy falha antes de qualquer efeito (inclusive antes de gravar a ckey) com instancia_divergente. Nao move a app: para isso, runner switch-instance. Nao combina com --all |
--force, -f |
flag | Nao | Forca redeploy mesmo se versao ja implantada |
--skip-tests |
flag | Nao | Pula testes apos deploy |
--verbose, -V |
flag | Nao | Output detalhado com logs tecnicos |
--all |
flag | Nao | Deploya todas as apps registradas |
-x, --exact |
flag | Nao | Desliga a resolucao por query: <app> vira nome exato, um alvo so |
-y, --yes |
flag | Nao | Confirma a operacao quando a query casa varios alvos do mesmo projeto. Nao autoriza atravessar projetos |
--port <N> |
int | Nao | Override de porta (v2.19.0). Persiste no state — re-deploys reusam. |
--no-port |
flag | Nao | Desabilita port mapping (strategy=none) so desta deploy (v2.19.0). |
--insecure |
flag | Nao | Bypass de pre-flight de secrets vazios (v2.19.0, NÃO RECOMENDADO). |
--wait-lock[=SEG] |
opção | Nao | v2.160.0+. Espera o lock da app em vez de falhar com lock_contended. Sem valor, até 600 s. Avisa uma vez quem segura. Para loops de manutenção que cruzam com o cron |
--force-unlock |
flag | Nao | Remove .deploy.lock antes de tentar o deploy (v2.26.4). Use quando stale-detection (PID gone OR > LOCK_MAX_AGE) nao disparou e o deploy ainda diz "Another deploy is in progress". |
--allow-missing-required |
flag | Nao | (v2.32.0+) Bypass do pre-flight de variaveis required: declaradas no manifest. Use apenas em rollouts de emergencia onde voce sabe que o container vai subir com placeholders. |
--local |
flag | Nao | (v2.52.0+) Deploya o estado que esta no disco sem re-sincronizar do repo. Pula o git reset do pull/ e o refresh do .deploy.yml a partir do pull/. Use quando editou arquivos localmente e quer deploya-los sem que o runner os reverta pra versao do repo. Nao combina com --all. |
--skip-health |
flag | Nao | (v2.54.0+) Pula a validacao de saude so nesta invocacao — equivale a healthcheck: mode: skip_check porem sem tocar no manifesto. Use quando o container sobe mas ainda nao ha Traefik/healthcheck valido no server. Redeploy futuro revalida sem reinstalar. |
*Use <app> ou --all, nao ambos.
Qual instancia e deployada (v2.55.1+)
A instancia efetiva e resolvida nesta ordem — a primeira que existir vence:
| Prioridade | Consulta | Fonte |
|---|---|---|
| 1 | state.instance |
state do registro em /opt/runner/state/<app>.yml, fixado por runner add --instance <N> |
| 2 | default do manifesto | .deploy.yml: production se declarada, senao a primeira instancia de instances: |
| 3 | production (literal) |
fallback quando o manifesto nao declara instancias |
runner deploy, runner deploy --all e runner fetch --deploy usam a mesma resolucao a partir da v2.55.1. Antes, os deploys manuais ignoravam o passo 1 e miravam sempre o default do manifesto.
Sem
productione semstate.instance, o passo 2 usa a primeira instancia declarada no.deploy.yml(deterministico desde a v2.55.2 —instancespreserva a ordem do YAML). Ainda assim, para deixar explicito, declareproduction, registre com--instance, ou use um manifesto por deploy (veja Dois deploys da mesma branch).
Deploy do estado local (`--local`)
Por padrao, todo runner deploy trata o repo como fonte da verdade: antes de subir o container ele re-sincroniza o pull/ (via git reset --hard) e re-copia o .deploy.yml do pull/ pra raiz. Isso sobrescreve edicoes locais — se voce editou o .deploy.yml na maquina e rodou deploy --force, sua mudanca e revertida pra versao do repo.
O --local deploya exatamente o que esta no disco, sem essa re-sincronizacao:
runner deploy meu-app --local --force- Naquele deploy o disco e soberano; nao persiste (o proximo deploy sem
--localvolta a sincronizar do repo). - Para uma mudanca duravel, commite no repo (branch
dist) — e o fluxo normal. - Para
.env/.secretsespecificamente, useenv_sync: skipno manifesto (torna os locais soberanos de forma permanente).
Pular a validacao de saude (`--skip-health`) (v2.54.0+)
Por padrao o deploy so e declarado bem-sucedido quando o healthcheck passa (fase 5). Se falhar, o runner faz rollback. Isso e o correto — mas ha um caso em que voce quer subir mesmo assim: o container sobe, porem ainda nao ha Traefik ou healthcheck valido no server (ambiente sendo montado). Sem uma valvula de escape, o deploy fica preso em rollback.
O --skip-health pula a validacao de saude so naquela invocacao:
runner deploy meu-app --skip-health- Equivale a
healthcheck: mode: skip_check, porem sem editar o manifesto do repo. - A app e declarada OK sem gating, o container sobe e o Traefik e roteado normalmente.
- Nao persiste: um redeploy futuro (ja com o ambiente pronto) volta a validar a saude — sem reinstalar a app.
- Emite um warn no log (
[HEALTH] --skip-health: pulando a validacao...) e o evento de notificacaoHealthSkipped.
Diferenca em relacao a mode: skip_check no .deploy.yml: aquele desliga a saude permanentemente para a app (todo deploy). O --skip-health e pontual — use quando o skip e temporario. Para desligar de forma duravel, prefira o modo no manifesto.
Tambem disponivel no primeiro deploy via runner add --deploy --skip-health (veja `runner add`).
Exemplos
Deploy Padrao
runner deploy --app /data/apps/usuario_meu-appDeploy por Nome
runner deploy meu-appDeploy de Versao Especifica
runner deploy meu-app --version v1.2.3Deploy de uma instancia especifica
Ate a v2.173.0 runner deploy nao tinha flag --instance. Desde a v2.174.0 ela existe, mas so confere: a instancia continua vindo da resolucao abaixo (fixada no registro via runner add --instance <N>), e uma instancia diferente da registrada falha com instancia_divergente. Veja Qual instancia e deployada.
# registra o app ja fixando a instancia
runner add --repo usuario/meu-app --branch main --instance staging --ckey "<ckey>"
# a partir daqui, o deploy honra staging automaticamente
runner deploy meu-app
# confere a instancia (v2.174.0+); falha com instancia_divergente se a app nao for staging
runner deploy meu-app --instance stagingDeploy sem Testes
runner deploy meu-app --skip-testsDeploy Forcado (Redeploy)
runner deploy meu-app --forceEm modo IMAGE, --force puxa a imagem mais recente do registry antes de
recriar o container. Exceção (v2.50.0): com pull_policy: never no manifesto
(imagem carregada localmente via docker load, sem registry), o pull é
pulado com log informativo — antes ele tentava e falhava com WARN em todo
deploy forçado.
Deploy com Output Detalhado
runner deploy meu-app --verboseDeploy de Todas as Apps
runner deploy --allOverride de Porta (v2.19.0)
# Override permanente (persiste no state — re-deploys reusam)
runner deploy myapp --port 9000
# Sem port mapping (strategy=none, container so na rede docker)
runner deploy myapp --no-port--port N sobrescreve qualquer alocacao sticky existente e persiste em state.network.published_port. Use isso pra "fixar" uma porta que voce quer permanente. As deploys subsequentes (manuais ou via fetch --deploy) vao reusar a mesma porta sem precisar --port de novo.
--no-port desabilita port mapping so desta deploy — nao persiste. Util pra debugging ou pra apps que so precisam estar na rede Docker (sem expor pro host).
Veja `runner ports` pra inspecionar/liberar/realocar portas alocadas.
Pre-flight de secrets vazios (v2.19.0)
Antes do container subir, o runner valida que cada secret declarado em .deploy.yml::secrets: tem valor nao-vazio em .runner/secrets.enc (apos decifrar). Se algum esta vazio, o deploy aborta:
runner deploy minha-api
# Error: deploy_blocked: secrets declared in .deploy.yml have empty values: ["APP_KEY"]
# Bypass: rerun with --insecure (NOT recommended).
# Or fill the values:
# runner env set <app> --key APP_KEY --secret --value <value>Resolução padrão:
runner env set minha-api --key APP_KEY --secret --value "base64:$(openssl rand -base64 32)"
runner deploy minha-api # agora passaBypass: runner deploy minha-api --insecure (so use conscientemente — geralmente significa que o app vai subir quebrado em runtime).
Resolve o caso onde Laravel /up retornava 200 mesmo com APP_KEY vazio, fazendo o runner declarar deploy bem-sucedido enquanto / retornava 500.
Pre-flight de variáveis obrigatórias (v2.32.0+)
O manifest aceita um campo required: no topo declarando variáveis que devem resolver pra valor não-vazio em .env ou .secrets antes do deploy começar:
required:
- NEXT_PUBLIC_SUPABASE_ANON_KEY
- DB_PASSWORDSe alguma estiver ausente ou vazia, runner deploy bailha fatal antes de tocar Docker:
runner deploy web-front
# Error: missing_required: variables required by the manifest are unset or
# empty: NEXT_PUBLIC_SUPABASE_ANON_KEY, DB_PASSWORD.
# Set them with `runner env set <app> <KEY> <VALUE>` (or `--secret -f` for
# secrets), then retry the deploy.Resolução padrão:
runner env set web-front NEXT_PUBLIC_SUPABASE_ANON_KEY anon-key-value
runner env set web-front DB_PASSWORD --secret -f "s3cr3t"
runner deploy web-front # agora passaBypass de emergência: runner deploy web-front --allow-missing-required (use apenas quando você sabe que o container vai subir com placeholders e isso é intencional).
Por que existe: o checker fecha o cenário onde Next.js (createSupabaseClient(URL, KEY) no middleware) crasha em startup com chave vazia → toda request 500 → healthcheck r.status<500 falha eternamente. Pré-v2.32.0, o runner ficava ~173s em retry loop antes de rolar back. Agora bailha em <1s.
`runner env validate ` como pre-deploy check
Sem --required na linha de comando, runner env validate cai no required: do manifest automaticamente:
runner env validate web-front
# Error: missing_required: ... NEXT_PUBLIC_SUPABASE_ANON_KEY, DB_PASSWORD.Use no seu pipeline CI antes de invocar deploy.
Util apos recovery do servidor. A partir da v2.1.0, o --all faz topological sort
baseado no campo depends_on: do .deploy.yml de cada app. Apps sem dependencias
sao deployadas primeiro; apps cujas dependencias falharam sao automaticamente puladas.
Ciclos de dependencia (a -> b -> a) sao detectados e abortam o --all com mensagem
listando os projetos envolvidos.
# Forca redeploy de todas
runner deploy --all --force
# Com output detalhado
runner deploy --all --verboseOutput do Deploy
Formato Padrao
O deploy exibe progresso com badges indicando cada etapa:
[1/6] [PREPARE] Preparando deploy...
[2/6] [ARTIFACT] Copiando artefatos...
[3/6] [ENV] Gerando ambiente...
[4/6] [SECURITY] Verificando segurança...
[5/6] [CONTAINER] Subindo container de teste...
[6/6] [PROMOTE] Promovendo versao...
═══════════════════════════════════════
✓ DEPLOY CONCLUIDO
App: meu-app/production
Versao: v1.2.0
URL: https://app.meusite.com.br
═══════════════════════════════════════Versao Ja Implantada
Se a versao ja esta implantada:
═══════════════════════════════════════
✓ JA IMPLANTADO
App: meu-app/production
Versao: v1.2.0
Use --force para reimplantar
═══════════════════════════════════════Modo Verbose (-V)
O modo verbose exibe logs tecnicos detalhados:
runner deploy meu-app --verboseInclui:
- Versao do Runner
- Caminho do config
- Logs de nivel INFO
- Detalhes de cada operacao
Pipeline de Deploy
O comando executa as seguintes etapas:
Prepare
- Verifica configuracao (
.deploy.yml) - Extrai versao do projeto
- Cria diretorio de versao
- Verifica configuracao (
Artifacts
- Copia artefatos para diretorio de versao
- Limpa workdir (remove arquivos desnecessarios)
Environment
- Processa templates de ambiente
- Gera
.enve.secrets - Encripta e persiste secrets no repo (se ckey configurada)
Test (Blue-Green)
- Sobe container de teste
- Executa health check
- Executa testes de seguranca
- Executa Playwright (se configurado)
Promote
- Atualiza symlink
current - Gera Traefik dynamic config
- Roteia trafego para nova versao
- Atualiza symlink
Cleanup
- Remove containers de teste
- Remove versoes antigas
Notify
- Envia notificacao (Telegram/Discord)
Comportamento
Rollback Automatico (v1.4.1)
O Runner faz rollback automatico dependendo da fase em que o deploy falha:
| Fase que falha | Impacto | Rollback |
|---|---|---|
| Fetch/Prepare (fases 0-1) | Container antigo nao foi tocado | Nenhum necessario — versao anterior continua ativa |
| Build (fase 3) | Container antigo nao foi tocado | Nenhum necessario — versao anterior continua ativa |
| Start (fase 4) com portas | Container antigo foi parado | Automatico — container anterior reiniciado |
| Start (fase 4) com Traefik | Container antigo nao foi tocado | Nenhum necessario — trafego continua na versao anterior |
| Health check (fase 5) | Container novo subiu mas nao e saudavel | Automatico — novo container removido, anterior reiniciado |
Em todos os casos, a notificacao de falha indica qual versao ficou ativa.
Cleanup do container quebrado (v2.26.4+)
Quando o Start da fase 4 falha sem versao anterior pra rollback (first deploy), o runner agora remove o container quebrado antes de retornar erro. Pre-v2.26.4, ele ficava UP segurando portas — bloqueava docker compose up paralelo com Bind for 0.0.0.0:PORT failed: port is already allocated.
Removal e best-effort (warn-only se falhar). Inspecione os logs antes de retentar:
docker logs <container_name> # logs do container que falhou (ainda disponiveis ate cleanup)
runner deploy <app> --verbose # retry com output completoLock recovery (v2.26.4+)
Cada deploy adquire <app_dir>/.deploy.lock. A stale-detection libera o lock automaticamente quando:
- O PID gravado nao existe mais (processo morreu sem release)
- O lock e mais velho que
LOCK_MAX_AGE_MINUTES(default 30min)
Se nenhuma dessas heuristicas dispara mas voce SABE que o deploy esta preso:
runner deploy <app> --force-unlock
# [deploy] --force-unlock: removed /data/apps/<app>/.deploy.lock
# (deploy continua normal apos a remocao)Quando NAO usar --force-unlock: se houver um runner deploy real rodando em outra sessao. Confirme com ps -ef | grep "runner deploy <app>" antes.
Protecao contra Retry Infinito (v1.4.1)
Quando o deploy automatico (fetch --deploy) falha, o Runner registra o commit que falhou no estado da app. Se o mesmo commit falhar novamente, o contador de retries incrementa. Ao atingir max_deploy_retries (padrao: 3), o Runner bloqueia o deploy automatico daquele commit.
Commit abc123 → deploy falhou (1/3)
Commit abc123 → deploy falhou (2/3)
Commit abc123 → deploy falhou (3/3)
Commit abc123 → bloqueado (aguardando novo push)
Commit def456 → novo commit, contador resetado → deploy executaPara configurar o limite:
# /opt/runner/config.yml
max_deploy_retries: 3 # padrao: 3, 0 = ilimitadoO deploy manual (runner deploy meu-app) nao e afetado pelo limite — sempre executa.
Health Check
O health check verifica:
- Container iniciou
- Endpoint
/healthretorna 200 - Container esta
healthy
O timeout e calculado automaticamente com base nos parametros de healthcheck do .deploy.yml:
timeout = start_period + (interval + timeout) * retries + bufferNotificacoes
Mensagens enviadas:
- Deploy Iniciado — ao comecar o pipeline
- Deploy Concluido — ao finalizar com sucesso
- Deploy Falhou — em caso de erro (inclui versao anterior ativa e erro extraido)
A mensagem de falha extrai a linha de erro real de outputs verbosos (Docker BuildKit, npm, compilador Rust) em vez de mostrar o output truncado.
Exemplo de notificacao de falha:
❌ Deploy Falhou
📦 Projeto: meu-app
🌐 Instance: production
🏷️ Versão: abc1234
🔄 Rollback: v1.0.0 (versão anterior mantida)
⚠️ Erro:
docker build failed: ERROR: process "/bin/sh -c npm run build" did not complete successfullyExit Codes
| Codigo | Significado |
|---|---|
| 0 | Deploy concluido |
| 1 | Deploy falhou (healthcheck, testes, build, rollback, lock, app inexistente: qualquer falha antes de o deploy concluir) |
| 3 | Deploy concluido com pendencia: o container esta no ar, healthy, e o estado foi gravado, mas um passo pos-deploy falhou (ex.: push dos secrets cifrados, manifesto_repo_divergiu). Nao e falha: nao tente de novo |
| 13 | manifesto_fixado: a operacao reescreveria o manifesto fixado no servidor e foi recusada antes de qualquer efeito (na 2.180.0 sai de runner upgrade e do manifest write sem --local) |
| 17 | manifesto_fixado_adulterado: a raiz <app>/.deploy.yml nao e o manifesto fixado. O deploy (e o rollback) nao comecou |
Erro de uso (flag desconhecida, argumento faltando) sai 2, do parser de
argumentos, antes de qualquer deploy.
Ate a 2.148.x o deploy com pendencia saia 1, igual a deploy falho; o 3
existe desde a 2.149.0. Os exits 13 e 17 existem desde a 2.178.0
(manifesto fixado; ver `runner manifest`).
Comandos de varias apps (fetch --deploy, deploy --all, deploy-all)
mantem a regra de exit que ja tinham: ali a pendencia conta como sucesso com
aviso. A app nao pausa nem soma retentativa.
Linha `RUNNER_RESULT`
Com RUNNER_RESULT_LINE=1 no ambiente, todo comando de deploy (deploy,
deploy-all, fetch --deploy, add --deploy, switch-instance --force)
termina com uma linha de maquina, a ultima do stdout. Sem a variavel a linha
nao sai (client antigo le o envelope JSON pela ultima linha); o exit code vale
sempre.
RUNNER_RESULT {"deploy":"ok","pos_deploy":[{"passo":"secrets_push","erro":"sem_token","mensagem":"..."}]}| Campo | Conteudo |
|---|---|
deploy |
ok ou falhou |
pos_deploy[] |
As pendencias. Vazio quando nada falhou depois do deploy |
pos_deploy[].passo |
O passo que falhou: secrets_push, manifesto_repo_divergiu, … |
pos_deploy[].erro |
sem_token, token_recusado ou outro |
pos_deploy[].mensagem |
Texto para o operador (app e comando de conserto). Nunca carrega segredo |
pos_deploy[].dados |
Opcional e aditivo. Em manifesto_repo_divergiu: {"commit", "campos"} (os nomes das chaves que mudaram) |
A pendencia manifesto_repo_divergiu (2.178.0+) sai quando o repositorio
mudou o manifesto e o deploy usou o manifesto fixado no servidor:
{"passo": "manifesto_repo_divergiu", "erro": "outro",
"mensagem": "implantado com o manifesto fixado (3a51b693); … — a mudança do repo não foi aplicada. Confira com `runner manifest status`",
"dados": {"commit": "d4e5f6", "campos": ["healthcheck"]}}Ver Quando o repositorio muda o manifesto.
Logs
# Ver historico
runner logs history --app /data/apps/meu-app
# Ver log do ultimo deploy
runner logs tail --app /data/apps/meu-appEstrutura Apos Deploy
/data/apps/meu-app/
├── .deploy.yml
├── .env
├── .secrets
├── .runner/
│ └── secrets.enc # Secrets encriptados (se ckey configurada)
└── src/
└── production/
├── v1.0.0/
├── v1.0.1/
└── current -> v1.0.1Secrets Encriptados
Se a app foi registrada com --ckey, o Runner encripta .env e .secrets apos cada deploy e persiste como .runner/secrets.enc no repositorio. Isso permite recuperacao zero-touch em caso de perda do servidor.
Consulte o guia Secrets e Encriptacao para detalhes completos.
Multiplos alvos e a fronteira do projeto (v2.80.0+)
<app> nao e um nome exato: e uma query, casada por segmento. runner deploy ccs
alcanca ccs/sys, ccs/front e ccs/worker — replicar o comando nos sistemas de um
projeto e o desenho do runner, nao um acidente.
| Situacao | Comportamento |
|---|---|
| 1 alvo | Executa direto, sem cerimonia |
| N alvos, mesmo projeto | Lista os alvos e pede confirmacao. -y, --yes dispensa |
| N alvos, projetos distintos | Terminal: pergunta. Cron/CI (sem TTY): aborta com exit != 0. Nenhuma flag autoriza |
O "projeto" aqui e a raiz antes do primeiro hifen: ccs-front e ccs-sys sao o
mesmo projeto (ccs); ccs-front e nfse-front nao sao.
Por que `--yes` nao autoriza atravessar
--yes e passado por habito, em script e cron. Se valesse para o cruzamento, o gate
morreria exatamente onde ninguem esta olhando. Uma query casa por segmento e deveria
cair dentro de uma familia — atravessar e anomalia, nao intencao.
ERROR deploy atravessa 2 projetos (ccs, nfse) em 4 alvos:
- ccs/sys
- ccs/front
- nfse/sys
- nfse/front
Uma query casa por segmento e deveria cair dentro de UMA família — atravessar aqui é anomalia, não intenção, e por isso nenhuma flag autoriza.
Se quis mesmo atingir vários projetos, o comando é `--all`.
Para um alvo só: -xEm terminal interativo o cruzamento pergunta em vez de abortar. Em cron/CI (sem TTY) aborta com exit != 0.
Saidas:
-x, --exact— desliga a query e mira um alvo so--all— a forma explicita de operar em massa (tem gate proprio)