Saltar para o conteúdo principal
50% de desconto todos os planos, tempo limitado. A partir de $2.48/mo
15 min left
IA e machine learning

Como agendar agentes de IA para rodar durante a noite em um VPS

S Por Sajjad 15 min de leitura
Schedule AI Agents Overnight: a dark terminal showing a 02:00 timestamp and a green exit code 0 line, next to a clock and a completed job card

Às 2 da manhã, uma tarefa agendada dispara em um VPS que nunca dormiu. Uma execução headless de claude -p processa uma tarefa na fila dentro de um repositório clonado sem perguntar nada a ninguém e então encerra. Quando você confere de manhã, há um commit esperando, ou um relatório, ou um log mostrando exatamente onde parou e por quê. Ninguém ficou olhando.

Isso é bem diferente de deixar um terminal aberto a noite toda e torcer para a conexão SSH sobreviver. Um ponto de falha comum em uma execução noturna do agente é o próprio host: um notebook entra em suspensão, a tampa fecha, a rede cai, ou uma atualização do sistema reinicia a máquina no meio da tarefa. Falhas de autenticação, erros de API e travamentos por permissão ainda podem matar o trabalho, mas um host sempre ligado elimina o modo de falha mais fácil.

Este guia cobre o mecanismo de verdade: as flags headless que cada CLI importante de agente de código traz, as duas formas de disparar uma execução agendada e qual escolher, o que o host embaixo precisa, e as proteções que impedem uma execução sem supervisão de custar ou quebrar mais do que você gostaria de ter que explicar depois.

A versão curta

  • Toda CLI importante de agente de código traz um modo não interativo documentado que executa um prompt até o fim e encerra. O Claude Code tem claude -p, o Codex CLI tem codex exec, e o Gemini CLI tem gemini -p. Isso não é uma gambiarra, é um recurso oficial.
  • O Claude Code também tem agendamento próprio: Routines, tarefas agendadas do Desktop e /loop. Para alguns leitores isso já basta, e dá menos manutenção do que um VPS.
  • O cron dá conta de uma tarefa noturna. Um temporizador systemd é o padrão melhor em uma máquina que pode reiniciar, porque Persistent=true recupera uma execução que o cron teria pulado em silêncio.
  • A CLI em si é leve porque a inferência acontece na API do provedor. Dimensione o VPS para os comandos que ele vai rodar (testes, builds, contêineres, jobs paralelos), não para o modelo.
  • São as proteções (ferramentas com escopo, um teto de turnos, ramificação por código de saída) que tornam seguro deixar um agendamento por conta própria. O agendamento sozinho não é o mecanismo de segurança.

O Que Vai Precisar

Deixe estas cinco coisas prontas antes de escrever uma única linha de crontab ou arquivo de unidade:

  • Um VPS que você acesse por SSH, rodando uma distribuição Linux baseada em systemd.
  • A CLI do agente instalada nesse VPS: Claude Code, Codex CLI ou Gemini CLI.
  • Uma credencial não interativa para a CLI que você escolher. O modo bare do Claude Code não lê nenhum login de conta, então ele precisa de ANTHROPIC_API_KEY no ambiente, ou um apiKeyHelper nas configurações. Uma execução normal em modo print, o Codex e o Gemini também podem usar as credenciais de login de conta documentadas.
  • Um repositório ou diretório de tarefas em que o agente vai atuar.
  • Acesso a shell com permissão para editar uma crontab ou escrever um arquivo de unidade do systemd.

Executar um agente sem nenhuma sessão anexada

Comparação dos modos headless: o Claude Code executa claude -p com saída text, json ou stream-json, o Codex CLI executa codex exec com um fluxo JSONL e uma política de sandbox, e o Gemini CLI executa gemini -p sem TTY

Toda CLI importante de agente de código traz um modo não interativo feito exatamente para isso. O Claude Code aceita -p, que também se escreve --print. O Codex CLI aceita codex exec. O Gemini CLI aceita -p, que também se escreve --prompt. Cada um aceita um prompt, executa até o fim e encerra. Sem loop de chat, sem terminal para deixar aberto, sem nada a que se reconectar.

O Claude Code consegue rodar sem uma sessão ativa? Sim. Passar -p roda o prompt em modo não interativo: o Claude Code executa até o fim, imprime o resultado e encerra. Não há loop de chat nem nada para manter vivo, e tudo roda sobre o mesmo Agent SDK que sustenta a CLI interativa, conforme a própria documentação do modo headless da Anthropic.

CLIFlag não interativaComportamentoSaída estruturada
Claude Code-p / --printExecuta o prompt até o fim, imprime o resultado, encerra--output-format definida como text, json ou stream-json
Codex CLIcodex execTransmite o progresso para stderr, escreve a mensagem final em stdout e encerra--json para um fluxo de eventos JSONL
Gemini CLI-p / --promptExecuta o prompt de forma não interativa e encerra--output-format json

Aqui o que mais importa são as flags do próprio Claude Code, porque é contra elas que você vai realmente escrever scripts. Duas delas deixam uma execução seguir sem parar para pedir uma permissão que ninguém está acordado para conceder: --allowedTools, que pré-aprova ferramentas específicas, e --permission-mode, que define a linha de base para toda a execução. --max-turns limita quantos turnos de agente uma execução pode dar antes de sair com erro.

--bare pula hooks, skills, plugins, servidores MCP e instruções de projeto como CLAUDE.md, em nome de uma execução em script mais rápida e determinística. Isso também significa que toda instrução da qual o trabalho depende precisa estar no prompt ou no comando. O modo bare também não lê o login da sua conta, então a documentação da Anthropic manda definir uma chave de API no ambiente antes de executá-lo. O Claude Code rejeita --bg de imediato quando combinada com -p, e rejeita --cloud da mesma forma quando você passa uma descrição de tarefa. Ele nomeia o conflito e para, em vez de fazer algo ambíguo.

Uma invocação completa; adapte o prompt e a lista de ferramentas à sua tarefa:

claude --bare -p "Review open PRs in this repo and summarize any blockers in NOTES.md" \
  --allowedTools "Bash(gh pr list *),Bash(gh pr view *),Bash(gh pr diff *),Read,Edit" \
  --permission-mode dontAsk \
  --max-turns 8 \
  --max-budget-usd 5.00 \
  --output-format json

Ajuste o orçamento e os padrões de comando à tarefa; este exemplo também presume que a autenticação do GitHub CLI já está configurada para a conta que o executa.

Se você está configurando o Claude Code em um VPS novo e quer o passo a passo para autenticá-lo em uma máquina sem navegador, isso é tratado separadamente em como autenticar o Claude Code em um servidor headless; a versão curta acima já basta para colocar uma execução agendada em funcionamento.

O modo exec do Codex CLI, descrito em a documentação de modo não interativo da OpenAI, aceita --sandbox para escolher uma política. read-only é o padrão, workspace-write deixa o agente escrever dentro do seu workspace, e --json transforma o stdout em um fluxo de eventos legível por máquina em vez de texto puro. Evite danger-full-access em um trabalho sem supervisão, a menos que o processo esteja isolado e esse risco seja deliberado.

O modo headless do Gemini CLI, documentado em a própria documentação headless do projeto, ativa automaticamente em um ambiente sem TTY, ou explicitamente com -p. Ele sai com um código diferente de zero específico para erro geral, erro de entrada ou limite de turnos atingido, em vez de um único código de falha genérico.

Onde o agendamento deve morar

Antes de todo esse trabalho de configuração: o fornecedor do agente talvez já agende isso para você. O Claude Code oferece três opções embutidas, e uma delas pode realmente servir melhor do que um VPS autogerenciado.

Nuvem (Routines)Tarefa agendada do Desktop/loop
Executa emA nuvem da AnthropicSua máquinaSua máquina
A máquina precisa estar ligadaNão obrigatórioNecessárioNecessário
Exige sessão abertaNão obrigatórioNão obrigatórioNecessário
Intervalo mínimo1 hora1 minuto1 minuto
Acesso a arquivos locaisNenhum, roda a partir de um clone novoAcesso totalAcesso total

A própria documentação da Anthropic sobre tarefas agendadas apresenta isso como uma escolha genuína entre três, não como uma hierarquia com o VPS no topo. Se a sua tarefa não precisa de estado local, tolera um piso de uma hora e você só usa o Claude Code, o Routines dá menos manutenção do que o que vem a seguir: a Anthropic o executa na nuvem a partir de um clone novo enquanto sua máquina está desligada.

/loop vale a pena conhecer, mas não serve para este caso, porque exige uma sessão aberta e ociosa, que é exatamente a restrição que você está tentando eliminar. A mesma documentação também aponta o GitHub Actions como quarta opção, para times cujo gatilho já vive na CI em vez de num agendamento preso a uma máquina específica.

O VPS autogerenciado ganha seu lugar quando o trabalho precisa de acesso completo ao sistema de arquivos local e às ferramentas, quando você quer o mesmo mecanismo funcionando de forma idêntica no Claude Code, no Codex CLI e no Gemini CLI, ou quando o intervalo permitido pelo Routines é grosseiro demais. Uma função serverless convencional costuma ficar desajeitada aqui, porque precisa restaurar credenciais, clonar o repositório e terminar dentro dos limites de execução da plataforma. Um runner de CI efêmero como o GitHub Actions continua sendo um terceiro caminho válido quando um checkout novo a cada execução é aceitável. Se você já tem hardware sempre ligado e ocioso, uma máquina de homelab também serve; a troca é depender da confiabilidade da sua rede doméstica e do seu acesso remoto em vez das de um provedor.

Cron ou um temporizador systemd?

Cron contra um temporizador systemd: à esquerda, uma linha de crontab e uma execução perdida que simplesmente é pulada; à direita, um par .service e .timer com recuperação via Persistent=true, registro no journald e controle de sobreposição por instância única

As duas ferramentas conseguem disparar o mesmo comando no mesmo horário, mas divergem no que acontece quando a máquina reinicia e em quanta configuração cada uma custa:

crontemporizador systemd
Peso da configuraçãoUma linha de crontabUm arquivo .timer e um arquivo .service
Recuperação de execuções perdidasNenhuma, uma execução pulada simplesmente se perdePersistent=true executa assim que o sistema volta
RegistroManual, você mesmo redireciona a saídaAutomático, capturado pelo journald
Ordenação de dependênciasNenhumaOrdenação completa do systemd com After= e Requires=

O cron simples dá conta de uma tarefa noturna em uma máquina que raramente reinicia. A pegadinha é o ambiente: o cron começa com um PATHPATH mínimo, não entra no seu repositório por você e vai alegremente iniciar uma segunda cópia enquanto a primeira ainda roda. Coloque o caminho do repositório, o comando restrito do agente e o carregamento de credenciais em um script wrapper protegido e então use flock para evitar execuções sobrepostas.

# /usr/local/bin/agent-nightly
#!/usr/bin/env bash
set -euo pipefail
export PATH=/usr/local/bin:/usr/bin:/bin
export ANTHROPIC_API_KEY="$(
  cat "$HOME/.config/agent-nightly/anthropic_api_key"
)"
cd /srv/myrepo
exec /usr/local/bin/claude --bare -p \
  "Run the nightly dependency audit and write the findings to NOTES.md" \
  --allowedTools "Bash(npm audit *),Read,Edit" \
  --permission-mode dontAsk \
  --max-turns 8 \
  --max-budget-usd 5.00 \
  --output-format json
# crontab -e
0 2 * * * /usr/bin/flock -n "$HOME/.local/state/agent-runs/nightly.lock" /usr/local/bin/agent-nightly >> "$HOME/.local/state/agent-runs/nightly.log" 2>&1

Crie uma única vez os diretórios de credenciais e de logs, e então torne o script wrapper executável:

install -d -m 700 \
  "$HOME/.config/agent-nightly" \
  "$HOME/.local/state/agent-runs"
touch "$HOME/.config/agent-nightly/anthropic_api_key"
chmod 600 "$HOME/.config/agent-nightly/anthropic_api_key"
"${EDITOR:-nano}" \
  "$HOME/.config/agent-nightly/anthropic_api_key"
sudo chmod 755 /usr/local/bin/agent-nightly

Cole apenas a chave de API no arquivo de credenciais. Não a coloque diretamente na crontab.

Um temporizador systemd exige mais configuração e te dá duas coisas que o cron não tem: registro no journald sem redirecionamento feito à mão, e Persistent=true. O exemplo abaixo presume que uma conta dedicada agent-runner é dona de /srv/myrepo. Guarde a chave de API em um arquivo de credenciais acessível só pelo root em vez de embuti-la na unidade.

Por o manual do systemd.timer, definir Persistent=true significa que "a unidade de serviço é acionada imediatamente se ela teria sido acionada pelo menos uma vez durante o período em que o temporizador ficou inativo." Assim, uma execução que teria disparado enquanto seu VPS reiniciava por causa de uma atualização de kernel dispara assim que ele volta, em vez de sumir em silêncio até o próximo horário agendado.

Crie o arquivo de credenciais acessível só pelo root que o serviço vai usar:

sudo install -d -m 700 /etc/agent-nightly
sudo touch /etc/agent-nightly/anthropic_api_key
sudo chmod 600 /etc/agent-nightly/anthropic_api_key
sudoedit /etc/agent-nightly/anthropic_api_key

Cole apenas a chave de API no arquivo.

# /etc/systemd/system/agent-nightly.service
[Unit]
Description=Nightly scoped agent run
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
User=agent-runner
Group=agent-runner
WorkingDirectory=/srv/myrepo
Environment=HOME=/home/agent-runner
Environment=PATH=/usr/local/bin:/usr/bin:/bin
LoadCredential=anthropic_api_key:/etc/agent-nightly/anthropic_api_key
ExecStart=/bin/sh -c 'export ANTHROPIC_API_KEY="$(cat "$CREDENTIALS_DIRECTORY/anthropic_api_key")"; exec /usr/local/bin/claude --bare -p "Run the nightly dependency audit and write the findings to NOTES.md" --allowedTools "Bash(npm audit *),Read,Edit" --permission-mode dontAsk --max-turns 8 --max-budget-usd 5.00 --output-format json'
StandardOutput=journal
StandardError=journal
UMask=0077
# /etc/systemd/system/agent-nightly.timer
[Unit]
Description=Run agent-nightly.service at 2am daily, catching up missed runs

[Timer]
# Uses the VPS's configured local timezone
OnCalendar=*-*-* 02:00:00
Persistent=true
Unit=agent-nightly.service

[Install]
WantedBy=timers.target

Recarregue o systemd, habilite o temporizador e rode o serviço uma vez imediatamente, para que problemas de credencial, permissão e caminho apareçam agora e não às 2 da manhã:

sudo systemctl daemon-reload
sudo systemctl enable --now agent-nightly.timer
sudo systemctl start agent-nightly.service
systemctl list-timers agent-nightly.timer
sudo journalctl \
  -u agent-nightly.service \
  -n 100 \
  --no-pager

Persistent=true é a diferença decisiva: o temporizador lembra de uma execução de calendário perdida em vez de descartá-la em silêncio.

O que o VPS realmente precisa

Esta é a parte que surpreende quem dimensiona isso pela primeira vez: a CLI em si é leve porque a inferência acontece na API do provedor. Mas o agente ainda pode disparar builds, testes, gerenciadores de pacotes, servidores de linguagem e contêineres localmente, então a carga do repositório é que define o piso real.

Considere 1 a 2 vCPU e 2 a 4 GB de RAM com armazenamento NVMe como ponto de partida para uma tarefa agendada leve. Repositórios grandes, compiladores, builds de Docker, suítes de teste ou execuções simultâneas podem exigir bem mais. O que força a aumentar o porte é o comando local mais pesado que o agente vai rodar, não o modelo por trás da API. Se você já roda cargas Docker neste VPS e quer um retrato mais completo do que orçar, dimensionar e proteger uma máquina de build percorre o mesmo trade-off para uma carga de trabalho sem supervisão diferente.

Mais uma coisa que vale planejar: uma execução sem supervisão gera logs toda noite, tenha dado errado alguma coisa ou não. Adicione logrotate se o cron escrever em um arquivo, e verifique os limites de retenção do journald em vez de presumir que os padrões cabem no disco do VPS.

Toda a abordagem depende de um host que esteja acordado às 2 da manhã e continue assim, faça o que fizer o seu notebook. Esse é exatamente o trabalho de um Linux VPS com acesso root. Nada o coloca para dormir, e você não o divide com os cron jobs de mais ninguém.

Ver planos Linux

Construa num VPS Linux com acesso root, NVMe e o poder do AMD EPYC.

Ver planos Linux

Como evitar que uma execução sem supervisão dê errado

A maior diferença entre uma execução agendada que funciona e uma que não funciona é se a tarefa está estreita o bastante para terminar sem um humano respondendo a uma pergunta no meio do caminho. Prompts ambiciosos travam esperando uma decisão que ninguém está lá para tomar; tarefas estreitas e autossuficientes terminam e encerram limpo.

As duas flags de permissão existem para que uma execução não trave em um pedido às 2 da manhã, mas acesso Bash cru não é uma barreira estreita: ele pode fazer quase tudo o que a conta de serviço pode. Prefira regras específicas por comando, como Bash(git status *), combine-as com --permission-mode dontAsk, e rode o serviço sob uma conta dedicada que não seja root. Contagem de turnos e gasto têm cada um seu teto: --max-turns limita quanto o agente pode divagar, e --max-budget-usd limita quanto uma única execução pode gastar em chamadas de API.

Dica: execute com --output-format json e registre o campo total_cost_usd de cada invocação. É o gancho mais limpo para acompanhar quanto uma execução agendada realmente custa por noite e para alertar quando uma execução custa bem mais que as outras. Vale os cinco minutos de configuração, porque o que está sendo acompanhado é a sua fatura, não uma abstração.

Estouros de custo em execuções sem supervisão não são hipotéticos. Em uma publicação no Hacker News, um usuário relatou uma fatura bruta de US$ 37.901,73 na AWS Bedrock vinda de um fluxo diário de agente de código em que o cache de prompts só funcionava em parte, deixando cerca de 6,47 bilhões de tokens de entrada sem cache. Isso aconteceu em outra stack, não no modo headless do Claude Code, mas mostra por que registro de custos e um orçamento rígido por execução pertencem ao agendamento.

Dica: o Claude Code sai com código 0 em caso de sucesso e com um código diferente de zero em caso de falha. Um script wrapper que verifica o status de saída pode te enviar uma notificação quando falhar, então uma noite ruim aparece na manhã seguinte em vez de três dias depois, quando você por acaso resolve olhar.

No mínimo, rode cada trabalho em um branch dedicado ou worktree descartável e exija revisão humana antes do merge. Credenciais com escopo restrito, isolamento do sistema de arquivos e controle do raio de impacto no nível do servidor são um assunto maior, que merece tratamento próprio em vez de um parágrafo colado no fim de um guia de agendamento.

São as proteções que tornam seguro deixar o agendamento em paz: o agendamento em si não é o mecanismo de segurança.

Quando o cron deixa de bastar

Um único prompt em um temporizador não precisa de nada além do que já foi coberto aqui. Três etapas encadeadas com um condicional, um retry e uma notificação no Slack precisam de outra coisa.

Três opções valem a pena conhecer, cada uma um degrau acima por um motivo diferente:

  • Dagu é o degrau mais leve: jobs autossuficientes definidos em YAML, com dependências em DAG, retries e uma interface web para acompanhar o que rodou.
  • n8n encaixa melhor quando a execução do agente é um nó entre várias integrações e notificações, e não o fluxo de trabalho inteiro.
  • Kestra é o mais pesado dos três, feito para orquestrar pipelines de dados e infraestrutura, e é a resposta certa quando agendar o agente faz parte de um pipeline maior em vez de ser o objetivo.

Para quem roda um único prompt por noite, os três são exagero, e vale dizer isso com todas as letras em vez de te empurrar para um arranjo mais pesado do que você precisa. Se uma cadeia de etapas acabar justificando um deles, Dagu, n8n, e Kestra têm implantação com um clique, o que é uma conveniência real exatamente no momento em que você está decidindo se o custo de configuração compensa.

Frameworks de orquestração multiagente como LangChain ou CrewAI são um assunto totalmente diferente: construir sistemas de agentes, e não agendar uma CLI que já existe.

Perguntas frequentes

O Claude Code consegue rodar sem uma sessão ativa?

Sim. Passar -p roda o prompt em modo não interativo: o Claude Code executa até o fim, imprime o resultado e encerra, sem loop de chat e sem sessão para manter aberta.

Preciso de um VPS se o Claude Code já tem Routines?

Nem sempre. O Routines roda na nuvem da Anthropic com a máquina desligada e parte de um clone novo, mas não consegue acessar arquivos que só existem na sua máquina e tem um intervalo mínimo de uma hora. Um VPS autogerenciado ganha seu lugar quando a tarefa precisa de arquivos locais, intervalos arbitrários ou um mecanismo que funcione igual nas CLIs de mais de um fornecedor.

Devo usar cron ou um temporizador systemd para um agente agendado?

Um temporizador systemd, se o VPS reiniciar em algum momento para manutenção. Persistent=true executa um trabalho que teria disparado durante a indisponibilidade assim que o sistema volta, algo que o cron não tem equivalente. O cron dá conta de uma tarefa noturna em uma máquina que fica sempre ligada.

Quanta RAM um agente de IA agendado precisa em um VPS?

Comece em torno de 1 a 2 vCPU e 2 a 4 GB de RAM para uma tarefa agendada leve, e depois dimensione pelo comando local mais pesado que o agente vai rodar. Builds, testes, Docker, repositórios grandes e execuções simultâneas pesam muito mais do que a inferência remota do modelo.

Rodar um agente de forma agendada muda a cobrança?

O agendamento não cria um modo de cobrança separado. O Claude Code -p pode usar credenciais de assinatura ou uma chave de API, mas --bare ignora o login da assinatura, então ele precisa de ANTHROPIC_API_KEY no ambiente, ou um apiKeyHelper nas configurações. O Codex e o Gemini seguem o método de autenticação que você configurou para a CLI de cada um. Como preços e termos de uso mudam rápido, confira os preços atuais do provedor e seus próprios dados de uso na hora de montar isso. Para execuções do Claude Code via API, você também pode registrar o campo total_cost_usd da saída JSON.

Partilhar

Discussão

Comentários

Inicie sessão para participar na discussão.

Mais do blogue

Continue a ler.

Pronto para implantar? A partir de $2,48/mês.

Cloud independente, desde 2008. AMD EPYC, NVMe, 40 Gbps. Reembolso em 14 dias.