A fila de build passou de dez minutos de espera antes do primeiro job começar a rodar, a fatura de minutos de CI dobrou em dois trimestres e alguém na retrospectiva sugeriu subir runners próprios. A decisão parece financeira, mas o custo por minuto é o critério menos confiável dos três que realmente pesam: tempo de manutenção, superfície de ataque e previsibilidade de performance.
Este comparativo trata dos dois modelos de execução de pipeline — runner gerenciado pelo provedor de CI e runner self-hosted em infraestrutura própria — assumindo GitHub Actions, GitLab CI ou equivalente. A mecânica é a mesma nos três.
O que muda tecnicamente entre os dois modelos
Runner hospedado entrega uma máquina virtual efêmera, criada do zero a cada job e destruída no fim. Imagem padronizada, cache de dependências gerenciado pelo provedor, isolamento garantido entre execuções. O time não administra nada e não controla nada: nem versão de kernel, nem tipo de disco, nem quanto tempo a VM leva para subir.
Runner self-hosted é um processo agente rodando em máquina do time — bare metal, VM em nuvem, pod em Kubernetes — que faz long-polling na API do provedor esperando job. Quem define CPU, memória, disco, imagem base e política de limpeza é o time. O agente pode ser efêmero (sobe um pod por job e morre) ou persistente (a mesma máquina executa milhares de jobs em sequência). Essa escolha entre efêmero e persistente pesa mais do que a escolha entre hospedado e self-hosted, e é onde a maioria dos times erra.
Runner persistente é rápido porque o cache de camadas Docker, os módulos de dependência e os artefatos de build sobrevivem entre execuções. Também é o modelo que acumula lixo de disco, mantém estado entre jobs de branches diferentes e transforma qualquer job comprometido em acesso persistente à máquina.
A conta real, item por item
Comparar preço de minuto contra preço de instância dá um resultado enganoso, porque as duas colunas medem coisas diferentes. A tabela abaixo separa o que cada modelo cobra e o que cada modelo consome de gente.
| Dimensão | Runner hospedado | Runner self-hosted |
|---|---|---|
| Modelo de cobrança | Por minuto de execução, com faixa gratuita mensal | Custo da instância enquanto ligada, independente de haver job |
| Ociosidade | Custo zero | Custo integral, salvo autoescala agressiva |
| Tempo até o primeiro comando | Dezenas de segundos, sujeito a fila em horário de pico | Praticamente imediato em runner persistente |
| Cache entre execuções | Depende de upload/download para o storage do provedor | Local, sem transferência de rede |
| Máquinas grandes ou GPU | Disponível em tiers caros | Livre, limitado só pelo orçamento de infra |
| Manutenção recorrente | Nenhuma | Patch, atualização do agente, limpeza de disco, monitoramento |
| Isolamento entre jobs | Total, por construção | Só com runner efêmero, e por decisão explícita |
A linha que quase sempre decide é a segunda. Pipeline de time pequeno concentra execução em janelas curtas — meio da manhã, fim da tarde — e fica parado o resto do dia. Instância ligada 24 horas para atender três horas de carga é desperdício que o preço por minuto do provedor dificilmente supera. O ponto de virada aparece quando a utilização passa de algo em torno de um terço do dia, ou quando o job precisa de máquina que o tier hospedado só oferece em faixa cara.
Vale medir antes de estimar. O tempo agregado de execução por mês está exposto na API do provedor e a soma real costuma surpreender: builds paralelos de matriz e jobs que rodam a cada push em branch de feature representam boa parte da fatura, e frequentemente podem ser cortados sem migrar nada.
gh api /repos/ORG/REPO/actions/runs --paginate -q '.workflow_runs[] | [.name, .run_started_at, .updated_at] | @tsv' — a diferença entre os dois timestamps, somada por workflow, mostra onde os minutos estão indo. Costuma ser um único workflow respondendo pela maior parte da conta.O custo que não aparece na planilha
Self-hosted transfere para o time responsabilidades que o provedor absorvia em silêncio. Nenhuma delas é difícil isoladamente; juntas consomem uma fração recorrente do tempo de alguém, e essa fração raramente entra na comparação.
- Disco. Runner persistente acumula imagens Docker, node_modules, artefatos e diretórios de workspace. Sem
docker system pruneagendado e política de retenção, o disco enche e o sintoma é build falhando de madrugada por espaço, não por código. - Versão do agente. O provedor deprecia versões antigas e o runner simplesmente para de receber jobs. Atualização precisa ser automatizada ou vira incidente.
- Deriva de imagem. A máquina de produção do CI acumula pacotes instalados manualmente para resolver um build específico. Seis meses depois ninguém sabe reconstruir o ambiente, e o pipeline passou a depender de estado não versionado.
- Autoescala. Manter custo baixo exige escalar de zero — via Actions Runner Controller no Kubernetes, grupo de autoescala com o agente no user data ou equivalente. É infraestrutura própria, com seus próprios modos de falha.
Regra prática: se ninguém no time tem tempo alocado para tratar o CI como serviço de produção, o self-hosted vai degradar. Não de imediato — em três ou quatro meses, quando a primeira falha de disco acontecer num sábado.
Segurança: o ponto em que o comparativo deixa de ser sobre custo
Runner self-hosted persistente executando workflow disparado por pull request de fork é a configuração mais perigosa dessa lista. Qualquer pessoa abre um PR alterando o arquivo de workflow, o job roda com o código dela na máquina do time e o processo tem acesso à rede interna, ao cache local e a tudo que jobs anteriores deixaram no disco. A documentação dos provedores desaconselha explicitamente essa combinação, e ela continua comum em repositório que virou público sem revisão do pipeline.
Três mitigações, em ordem de eficácia:
- Runner efêmero, um por job, sem reuso de máquina — resolve o problema estrutural em vez de contorná-lo.
- Exigir aprovação manual para workflows disparados por colaboradores externos, configuração disponível nas próprias settings do repositório.
- Isolar o runner em sub-rede sem rota para ambientes internos, com credenciais de curta duração via OIDC em vez de secrets estáticos.
Runner hospedado elimina essa classe inteira de risco porque a VM nasce e morre com o job. Em compensação, cria dependência da postura de segurança do provedor e obriga a expor endpoints internos caso o pipeline precise alcançar rede privada — o que normalmente leva a um túnel ou a um runner self-hosted só para os jobs de deploy.
O arranjo que costuma vencer
Na maioria dos times que mantêm sistema em produção, o desenho híbrido resolve melhor que qualquer extremo: runner hospedado para tudo que é disparado por push e por PR — lint, teste unitário, build de imagem — e um pool self-hosted pequeno, efêmero, com acesso à rede privada, exclusivo para jobs de deploy e de teste de integração que precisam de banco interno.
Um exemplo de segmentação por label, direto no workflow:
jobs:
test:
runs-on: ubuntu-latest # hospedado, efemero por construcao
deploy:
runs-on: [self-hosted, linux, deploy, ephemeral]
environment: production # exige aprovacao antes de executar
Essa separação mantém a superfície de ataque restrita a jobs que só rodam a partir da branch principal, deixa o volume de execução no modelo que cobra por uso e evita administrar frota de máquinas para rodar teste unitário.
Migrar tudo para self-hosted se justifica em dois cenários específicos: build que depende de hardware indisponível no tier hospedado — compilação nativa pesada, GPU, macOS em volume — ou utilização sustentada alta o suficiente para que a instância reservada saia mais barata que o minuto avulso com folga confortável. Fora desses casos, o ganho financeiro é pequeno e o passivo operacional é permanente.
Próximo passo concreto: extraia o consumo por workflow dos últimos 90 dias, identifique o job que responde pela maior fatia e verifique se ele precisa mesmo rodar a cada push. Reduzir gatilho e paralelismo desnecessário costuma cortar mais da conta do que trocar de modelo de execução — e não adiciona nenhuma máquina para manter.
Deixe um comentário