
Sessenta contêineres em um servidor
Uma caixa bare-metal executa dezenas a centenas de contêineres Hoody. KSM e BTRFS dedup fazem o custo marginal próximo a zero.
Versione `.hoody/crontab` no repositório, ao lado dos seus jobs. Quando o script de deploy sobe um contêiner para `main`, `feature/billing-v2` ou qualquer branch de preview, ele faz PUT desse arquivo na Cron API do novo contêiner. O agendamento embarca com a branch — e desaparece quando ela some.
um arquivo por branch · mesmo caminho em todo repositório · sem servidor cron compartilhado
Cada contêiner de branch roda Hoody Cron. O script de deploy lê o crontab versionado e faz PUT no endpoint raw-crontab do novo contêiner. O contêiner roda o agendamento que o arquivo descreve — nada mais, nada menos.
#!/bin/sh
# Provisiona um contêiner novo para esta branch.
BRANCH=$(git branch --show-current)
CTR=$(hoody containers create --from main-snapshot)
# Substitui o crontab do contêiner pelo que está no repositório.
curl -X PUT --data-binary @.hoody/crontab \
-H "Content-Type: text/plain" \
https://$CTR-cron-1.hoody.com/users/root/crontab
# Pronto. O agendamento da branch vive no contêiner dela.
echo "deployed $BRANCH → $CTR"# Endpoint raw-crontab do Hoody Cron — substitui o arquivo inteiro atomicamente.
PUT /users/root/crontab HTTP/1.1
Host: ctr_4d72b9-cron-1.hoody.com
Content-Type: text/plain
0 2 * * * /srv/jobs/billing-rollup-v2.sh
*/15 * * * * /srv/jobs/sync-stripe.py
@hourly curl -fsS http://localhost/healthz
*/5 * * * * /srv/jobs/diff-v1-v2.sh
HTTP/1.1 200 OK
# 200 OK: daemon cron recarrega, agendamento ativo em menos de um segundo.O crontab é dado que a branch carrega, não estado que o servidor cron lembra. Apague o contêiner e não sobra entrada para limpar — o arquivo foi com o disco.
Quando o agendamento vira um arquivo no repositório, três categorias de trabalho desaparecem.
Quando você muda `billing-rollup.sh` para v2, o novo agendamento entra no mesmo pull request. O revisor vê a linha cron bem ao lado do script. Reverta um commit e o agendamento reverte junto.
Contêineres de branch são efêmeros. Quando você faz merge ou fecha a branch, derruba o contêiner. O crontab vivia dentro dele, então o agendamento desaparece sem zelador — não há servidor cron compartilhado segurando entradas obsoletas.
Um job experimental por hora em `experiment/llm-rollups` roda no próprio contêiner com seu próprio sistema de arquivos. O daemon cron de staging nunca o vê; o de produção nunca o vê. Não existe `if BRANCH_ENV` dentro dos próprios jobs.
O modelo padrão de "um crontab gerenciado pelo ops" e o modelo amarrado à branch falham em direções opostas. Mesmo job, raio de impacto bem diferente.
A diferença não é um recurso — é onde o agendamento mora. Um arquivo que a branch carrega, contra uma linha em uma tabela compartilhada da qual a branch toma emprestado.
Cron por contêiner é uma superfície REST de verdade — três famílias de endpoints, sintaxe cron padrão, isolamento total por usuário. Números da especificação da Cron API, não benchmarks inventados.
Cada contêiner de branch tem o próprio crontab por usuário. PUT no arquivo inteiro, GET para recuperar, substitua atomicamente. Sem tabela de agendamento compartilhada por trás.
Crontab raw (GET/PUT), entradas gerenciadas (POST/PATCH/DELETE com UUIDs e `expires_at`) e listagem por usuário. Escolha o que seu script de deploy precisa.
Padrão `min hora dia mes dow` mais macros: `@hourly`, `@daily`, `@weekly`, `@monthly`, `@yearly`. A mesma sintaxe que seu `.hoody/crontab` já usa.
Conforme a Hoody Cron API: GET/PUT /users/[user]/crontab e POST/PATCH/DELETE /users/[user]/entries na URL do serviço cron de cada contêiner.
O agendamento vive ao lado do código que roda nele, no mesmo contêiner, na mesma branch.
Seis lugares onde o agendamento cron costumava morar, nenhum deles ao lado do código. O crontab atrelado à branch torna todos eles redundantes.
Pare de sincronizar agendamentos entre sistemas. Versione o crontab. Deixe a branch carregar.