Era uma sexta à noite e eu estava orgulhoso do que tinha acabado de construir. Tinha acabado de subir o GitLab Community Edition no meu Raspberry Pi 4B — um sonho antigo de ter um ambiente de desenvolvimento completo inteiramente sob meu próprio controle. Git, CI/CD, registry, tudo numa caixinha na prateleira.

Fui dormir.

Sábado de manhã, abri o terminal para dar push em algo e encontrei… silêncio. O Raspberry Pi não respondia. SSH em timeout. Nenhum ping. Fui até a prateleira, olhei o LED verde piscando devagar, e entendi: o consumo de memória do GitLab — provavelmente um vazamento lento agravado por jobs em background competindo por CPU — tinha sufocado todos os outros serviços do nó até o kernel não ter escolha a não ser intervir. O Pi tinha reiniciado, e sem espaço suficiente para trazer tudo de volta, simplesmente ficou parado ali.

Frustrante nem começa a descrever.


Por que o GitLab afundou o Pi

O GitLab CE é uma peça de engenharia impressionante. Também é uma que assume que você tem pelo menos 4GB de RAM disponíveis só para ele — de preferência 8GB. O Raspberry Pi 4B tem 4GB no total, divididos entre o sistema operacional, processos em background, e tudo mais que você quer rodar.

A stack do GitLab inclui: Puma (Ruby), Sidekiq, seu próprio PostgreSQL, seu próprio Redis, Gitaly, GitLab Workhorse, NGINX interno. Cada um desses processos tem um consumo de memória considerável. Juntos, transformaram meu Pi num problema de escalonamento de processos — irônico para um dispositivo que eu usava para estudar exatamente isso na faculdade.

A lição foi simples e cara: hardware modesto exige software modesto. Você não pode pegar uma solução desenhada para servidores e esperar que caiba num ARM com recursos limitados.


Encontrando o Gitea

Depois de pesquisar alternativas, cheguei ao Gitea. Escrito em Go. Binário único. Nenhuma dependência externa além de um banco de dados. O processo inteiro — incluindo interface web, API, e gerenciamento de repositórios — rodando em menos de 150MB de RAM em idle.

Essa diferença não é acidental. É uma consequência direta da linguagem e da filosofia de design. Go compila para um binário estático, sem runtime externo, sem interpretador. O tempo de boot é quase instantâneo, o consumo é previsível, e o comportamento sob pressão de memória é consideravelmente mais suave do que uma aplicação Ruby que depende de dezenas de gems.

Rodei o Gitea como um Pod dentro do meu cluster k3s, com um PVC para persistência. O Pi nunca mais reiniciou.


A arquitetura atual

O ambiente que construí tem algumas camadas que se encaixam bem:

k3s no Raspberry Pi 4B como a plataforma de orquestração. k3s é Kubernetes para quem não tem um data center — consome menos de 500MB e inicia em segundos. O Traefik vem embutido como ingress controller, o que elimina uma peça inteira da stack.

Gitea como servidor Git e interface de CI. Organizações dentro do Gitea mapeiam diretamente para namespaces do Kubernetes — uma disciplina que mantém contextos separados e facilita entender o que pertence a quem. org/projeto no Gitea corresponde ao namespace org no cluster.

Gitea Runner como executor de pipeline. Cada push dispara o runner, que roda os passos dentro de um container no próprio cluster. Sem agente externo, sem dependência de um serviço de nuvem.

nerdctl + buildkitd para builds de imagem. Substitui o Docker no ambiente do runner — o nerdctl fala a mesma API, mas se integra diretamente com o containerd do k3s. As imagens são construídas com cache local em disco e carregadas diretamente no namespace k8s.io do containerd, sem precisar de registry intermediário.

Traefik expondo serviços via IngressRoute, com Cloudflare Tunnel provendo acesso externo sem abrir portas no roteador.


O problema do npm e a virada para o Go

Até esse ponto, as coisas estavam razoavelmente sob controle. O problema apareceu quando tentei integrar uma aplicação frontend em Node.js no pipeline.

npm install numa aplicação moderna é uma experiência… particular. Centenas de pacotes, dependências transitivas, arquivos que se resolvem no momento da instalação, scripts de build que podem fazer praticamente qualquer coisa. Num ambiente com disco lento (cartão SD) e CPU limitada (ARM Cortex-A72), um npm install + npm run build pode levar vários minutos — e consumir memória de um jeito que lembra desconfortavelmente o GitLab.

O problema não é só performance. É imprevisibilidade. O node_modules de um projeto pode ter 500MB ou 2GB dependendo das dependências. O tempo de build varia com a rede, com as versões exatas de pacote resolvidas pelo lockfile, com flags de otimização. Cachear isso corretamente num ambiente com restrição de armazenamento é um problema em si.

Foi quando comecei a preferir Go para projetos novos.

A diferença é estrutural. Go compila estaticamente. O artefato final é um binário único sem dependências de runtime. go build é determinístico — dado o mesmo código e as mesmas dependências, produz o mesmo binário. O tempo de compilação é razoável mesmo em ARM. E o binário resultante tem megabytes, não gigabytes.

Em runtime, um serviço Go típico consome dezenas de megabytes de RAM em idle. Um servidor Node.js com o mesmo propósito geralmente começa em 100–200MB só para o processo do interpretador. Em hardware com restrições reais, essa diferença se traduz diretamente em quantos serviços você consegue rodar simultaneamente — e quantas noites você dorme tranquilo sem o Pi reiniciar.

A tipagem estática do Go também contribui para o que chamo de determinismo comportamental: erros de tipo são pegos em tempo de compilação, não em runtime. Num pipeline de CI, isso significa binários que passam no build têm um contrato mais forte com o ambiente em que vão rodar. Menos surpresas às três da manhã.


O que roda hoje

O ambiente atual hospeda:

  • Sites estáticos em Hugo, com deploy automático no push
  • Documentação Docusaurus, buildada e servida como estático
  • Serviços em Go com pipelines de build e deploy no cluster
  • O próprio Gitea e sua infraestrutura

Tudo isso num Raspberry Pi 4B que cabe na palma da mão, consome menos de 15W, e não reinicia desde que o GitLab saiu de cena.

Às vezes o melhor upgrade de hardware é escolher o software certo.