DISABLE_REGISTRATION = true. Você cria as contas; ninguém se cadastra sozinho. Se precisar de cadastros ocasionais, use o fluxo de convites em vez de abrir a porta.

Seu próprio servidor Git.
O repositório é o organograma.
Um repositório privado é um registro datado do que você está construindo, com quem você está construindo e em que ponto você está — e em uma plataforma, esse registro fica nas mãos de uma empresa, na jurisdição de outra pessoa. Seis etapas para movê-lo para uma máquina que você aluga de forma anônima: Forgejo atrás do Caddy, PostgreSQL por baixo, push via SSH em uma porta só sua, um runner de Actions que nunca vê os segredos de produção, e um backup que você já restaurou de verdade. A máquina custa $3.90 por mês. Testado em Debian 12.
- 01
Escolher
Repositório nu, Forgejo, GitLab
- 02
Provisionar
Portas decididas antes de inicializar
- 03
Implantar
Forgejo, Postgres, Caddy
- 04
Enviar
SSH, chaves de implantação, assinaturas
- 05
Construir
Um runner, mantido à distância
- 06
Keep
Espelhos, backups, atualizações
O código é a parte menos interessante. Os metadados são o ativo.
Quase todo argumento sobre auto-hospedar o Git é um argumento sobre o código-fonte, e o código-fonte é a parte que menos importa. A maior parte dele não tem nada de especial, boa parte é apenas uma camada fina sobre bibliotecas de código aberto, e se vazasse amanhã o dano prático costumaria ser constrangimento, não prejuízo real. É por isso que "meu código não é secreto" parece uma resposta completa — e por que não é.
O que cerca o código é a parte interessante. Um repositório é um registro de trabalho datado, atribuído e de alta resolução: os horários dos commits revelam quando você dorme, as linhas de coautoria revelam com quem você trabalha, os nomes de branch revelam produtos que ninguém anunciou, um rastreador de issues revela o que está quebrado e o quão gravemente, e um primeiro commit data o projeto inteiro. Leia cem repositórios dessa forma e você não está mais lendo software — você está lendo uma empresa, seu roadmap, seu quadro de funcionários e seus problemas.
Em uma plataforma, esse registro não é seu em nenhum sentido relevante. Ele fica em uma infraestrutura que você não controla, sob uma conta vinculada a uma identidade que você verificou, em uma jurisdição cujo processo legal o alcança, vasculhado por sistemas cujo escopo é definido por um documento que pode ser alterado. Nada disso exige má-fé de ninguém: é apenas o que significa alugar o rastreador. Quatro arranjos, e o que cada um deles realmente expõe:
| Onde o repositório vive | Quem guarda o histórico | Vinculado à sua identidade legal | Como isso dá errado |
|---|---|---|---|
| Uma plataforma, conta pessoal | A plataforma, em sua própria jurisdição | Sim — e-mail, cobrança, muitas vezes um número de telefone | Uma suspensão leva todo remoto junto |
| Uma plataforma, conta de organização | A plataforma, além de quem administra a organização | Sim, e também ao seu empregador | O desligamento remove seu acesso ao seu próprio trabalho |
| Auto-hospedado, em um hyperscaler | Você, na camada de aplicação | Sim — a conta de cobrança é a identidade | O snapshot vive na conta que pode ser encerrada |
| Auto-hospedado, em uma VPS sem KYC | Você, e só você | Não — sem documento, sem cartão, pago em cripto | Agora você é o sysadmin. Um backup ruim é definitivo |
A terceira linha é a que pega de surpresa as equipes que acham que já resolveram essa questão. Uma forja que você mesmo instalou, em uma máquina alugada com o cartão da empresa sob uma conta corporativa verificada, tirou a aplicação da plataforma e deixou a identidade exatamente onde estava. Isso é um ganho real de controle e absolutamente nenhum ganho em exposição.
E a quarta linha declara seu próprio custo com todas as letras, porque o resto deste guia é basicamente sobre como pagá-lo. O modo de falha da auto-hospedagem não é vigilância, é perda. Uma plataforma mantém backups em que você nunca pensa. O seu servidor mantém os backups que você configurou. A etapa 06 não é o acabamento opcional do final — é a etapa que torna as outras cinco seguras.
Quanta forja você precisa? A resposta honesta costuma ser "nenhuma".
Comece pela menor coisa que resolve o seu problema, porque toda camada acima dela é um serviço que você vai ter que operar. Um repositório nu via SSH é um único comando em um servidor que você já tem, e ele te dá um remoto Git de verdade, com autenticação, histórico e acesso de push. Nenhuma aplicação web, nenhum banco de dados, nenhuma esteira de atualizações, nada para corrigir além do sshd. Se você é uma pessoa só que quer tirar seus commits de um laptop e de uma plataforma, essa é uma resposta completa, e você pode parar de ler depois da etapa 02.
# On the server, as an unprivileged user:
mkdir -p ~/git/project.git && cd ~/git/project.git
git init --bare
# On your laptop:
git remote add origin ssh://user@git.example.com/~/git/project.git
git push -u origin main
Você precisa de uma forja quando uma segunda pessoa entra em cena, ou quando você quer qualquer uma das quatro coisas que um diretório de repositórios nus não consegue fazer: navegar pelo histórico em um navegador, revisar mudanças como pull requests, acompanhar issues ao lado do código, e conceder a uma pessoa acesso a um único repositório. Isso é um salto genuíno em capacidade, e um salto genuíno em peso operacional. Escolha com cuidado:
| Propriedade | Repositório nu via SSH | Forgejo | Gitea | GitLab CE |
|---|---|---|---|---|
| Interface web, pull requests, issues | Nenhum | Sim | Sim | Sim, e muito mais |
| Memória em repouso | Praticamente zero | Algumas centenas de MB | Algumas centenas de MB | Vários GB, antes do CI |
| CI embutido | git hooks, se você os escrever | Forgejo Actions | Gitea Actions | GitLab CI, o mais maduro |
| Quem dirige o projeto | O projeto Git | Codeberg e.V., uma organização sem fins lucrativos | Uma empresa com fins lucrativos, open core | Uma empresa de capital aberto, open core |
| Licença | GPLv2 | GPLv3+ desde a v9.0 (MIT antes) | MIT, com complementos proprietários | Núcleo MIT, planos proprietários |
| Escolha-o quando | Você está sozinho e quer manutenção zero | Você quer uma forja de verdade em uma máquina pequena | Você já usa e funciona | Você precisa do conjunto de recursos empresarial |
Forgejo e Gitea compartilham um ancestral e ainda parecem quase idênticos no uso do dia a dia. O Forgejo se tornou um hard fork no início de 2024 e é mantido pela Codeberg e.V., uma associação registrada sem fins lucrativos em Berlim; o Gitea é desenvolvido por uma empresa com fins lucrativos, dona de sua marca registrada, que pede aos contribuidores a cessão dos direitos autorais. Os dois são bons softwares. Só um deles tem uma mantenedora cujos incentivos combinam com o motivo pelo qual você está lendo esta página.
O restante deste guia usa o Forgejo. Tudo o que está nas etapas 02 a 06 se aplica quase palavra por palavra ao Gitea — o nome do contêiner e algumas variáveis de ambiente mudam, mas a estrutura, não.
Decida as portas antes que a forja as decida por você. Duas delas são SSH.
Peça a máquina primeiro. No painel: Order → VPS → Sentinel, imagem Debian 12, em qualquer bastião onde você queira que o histórico viva — Helsinki, Stockholm, Oslo ou Reykjavík. Nenhum endereço de e-mail é exigido para abrir a conta, e a fatura é paga em Monero, Bitcoin, Lightning ou qualquer outro ativo aceito. Isso importa mais aqui do que na maioria das cargas de trabalho: uma forja é o único serviço cuja proposta de valor inteira é que o registro do seu trabalho não esteja vinculado a um nome.
Depois rode o checklist de hardening da primeira hora — SSH somente com chave em uma porta alterada, atualizações automáticas, um firewall que nega tudo por padrão — porque tudo o que vem a seguir presume uma máquina que ainda não é de outra pessoa.
Uma forja não é um site estático com login. É uma aplicação web pública com um motor de execução de código aparafusado nela. Dois padrões, se deixados como estão, a transformam em um serviço gratuito para estranhos: registro aberto, que enche a instância de contas spam poucos dias depois de o certificado aparecer nos registros de transparência, e um CI que roda workflows de qualquer pessoa que possa abrir um pull request, que é como um runner auto-hospedado acaba minerando criptomoeda para outra pessoa. Nenhum dos dois é um ataque sutil, e nenhum dos dois é raro. Os dois se fecham com uma configuração cada, nas etapas 03 e 05 — mas decida agora que você vai fechá-los, porque a janela entre "funciona" e "está sendo usado" se mede em dias.
Aponte um hostname para a máquina — um registro A, e um AAAA se você quiser que ela seja acessível via IPv6 — e deixe isso se propagar antes de começar, porque o proxy reverso vai tentar obter um certificado para esse nome na primeira vez que subir, e um desafio que falha é uma forma confusa de começar. Depois escreva as regras de firewall enquanto ainda não há nada escutando.
O problema dos dois SSH. Uma forja fala SSH para o Git, e o seu servidor já fala SSH para administração. Os dois não podem ser donos da porta 22 ao mesmo tempo, e misturá-los é como as pessoas acabam com um daemon administrativo que também precisa entender de Git. Mantenha-os separados: administração na porta alterada do guia de hardening, Git em uma porta pública dedicada que mapeia para dentro do contêiner. 2222 é a escolha convencional, e não há nada de sagrado nela.
ufw default deny incoming
ufw allow 47222/tcp # administrative SSH, moved
ufw allow 80/tcp # ACME HTTP-01 challenge
ufw allow 443/tcp # the web interface
ufw allow 2222/tcp # git over SSH, into the container
ufw enable
ufw status numbered
# Nothing else is open. In particular: PostgreSQL stays on the Docker
# network and is never published to the host, let alone the internet.
Instale o Docker a partir do repositório oficial, em vez do pacote da distribuição, para garantir que o Compose v2 esteja presente e a versão esteja atualizada. Depois crie um diretório para a stack e faça dele o único lugar na máquina que guarda estado — é esse o diretório que a etapa 06 vai fazer backup.
curl -fsSL https://get.docker.com | sh
docker compose version
install -d -m 0750 /srv/forgejo
cd /srv/forgejo
Três contêineres. Um deles nunca pode ser acessível.
Forgejo, PostgreSQL e Caddy. O Forgejo guarda os repositórios e serve a interface, o PostgreSQL guarda tudo que não é um objeto Git, e o Caddy termina o TLS e obtém o certificado sem que seja preciso pedir duas vezes. O PostgreSQL deliberadamente não é publicado para o host: ele só é acessível pela rede do Compose, por nome, a partir do único contêiner que precisa dele.
/srv/forgejo/.env
FORGEJO_DOMAIN=git.example.com
FORGEJO_VERSION=15 # the LTS line; 16 is current stable
POSTGRES_PASSWORD=$(openssl rand -base64 32) # paste the value, do not leave the $( )
/srv/forgejo/docker-compose.yml
services:
forgejo:
image: codeberg.org/forgejo/forgejo:${FORGEJO_VERSION}
container_name: forgejo
environment:
USER_UID: 1000
USER_GID: 1000
FORGEJO__database__DB_TYPE: postgres
FORGEJO__database__HOST: db:5432
FORGEJO__database__NAME: forgejo
FORGEJO__database__USER: forgejo
FORGEJO__database__PASSWD: ${POSTGRES_PASSWORD}
FORGEJO__server__DOMAIN: ${FORGEJO_DOMAIN}
FORGEJO__server__ROOT_URL: https://${FORGEJO_DOMAIN}/
FORGEJO__server__SSH_DOMAIN: ${FORGEJO_DOMAIN}
FORGEJO__server__SSH_PORT: "2222" # what clones will say
FORGEJO__server__SSH_LISTEN_PORT: "22" # what the container listens on
FORGEJO__service__DISABLE_REGISTRATION: "true"
FORGEJO__repository__DEFAULT_PRIVATE: private
FORGEJO__security__INSTALL_LOCK: "true"
restart: unless-stopped
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "127.0.0.1:3000:3000" # web, reached only by Caddy
- "2222:22" # git over SSH, public
depends_on:
- db
db:
image: postgres:16-alpine
container_name: forgejo-db
environment:
POSTGRES_USER: forgejo
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: forgejo
restart: unless-stopped
volumes:
- ./postgres:/var/lib/postgresql/data
# No ports: block. This is the point.
caddy:
image: caddy:2-alpine
container_name: forgejo-caddy
restart: unless-stopped
network_mode: host
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy-data:/data
- ./caddy-config:/config
/srv/forgejo/Caddyfile
git.example.com {
encode zstd gzip
request_body {
max_size 512MB # raise it if you push large repositories
}
reverse_proxy 127.0.0.1:3000
}
As duas linhas de SSH são as que as pessoas mais erram, então vale a pena lê-las duas vezes. SSH_LISTEN_PORT é a porta dentro do contêiner, onde o servidor SSH embutido do Forgejo escuta. SSH_PORT é a porta que o Forgejo imprime nas URLs de clone que mostra a você — ela precisa corresponder ao lado público do mapeamento de porta. Se elas ficarem dessincronizadas, tudo funciona, exceto a única coisa que os usuários copiam e colam.
Suba tudo, e depois crie o administrador pela linha de comando. Não use o instalador web. Em uma instância recém-criada, ele fica acessível a qualquer pessoa que encontre o hostname, e a primeira conta criada por ele vira a administradora — uma corrida que você não precisa disputar. O INSTALL_LOCK no arquivo acima o bloqueia antes mesmo de o contêiner iniciar.
docker compose up -d
docker compose logs -f forgejo # wait for "Starting new Web server"
docker compose exec -u git forgejo forgejo admin user create \
--admin --username you --email you@example.com --random-password
Abra o hostname em um navegador, faça login com a senha impressa e troque-a. Depois percorra as configurações que transformam uma instância funcional em uma instância fechada — a maioria já está definida pelo bloco de variáveis de ambiente acima, e vale a pena confirmar cada uma delas em vez de simplesmente presumir:
DEFAULT_PRIVATE = private. Novos repositórios começam fechados. Publicar passa a ser um ato deliberado, e não a consequência de uma configuração padrão.
REQUIRE_SIGNIN_VIEW = true esconde até os repositórios públicos de visitantes sem login. Certo para uma forja privada de equipe; errado se você quiser publicar alguma coisa.
Ative isso na conta do administrador antes de fazer qualquer outra coisa. A forja agora é a máquina que pode reescrever cada branch que você tem.
Um caminho que vale a pena memorizar: a configuração ativa fica em ./data/gitea/conf/app.ini no host. O Forgejo manteve o nome de diretório gitea por compatibilidade com instalações existentes, o que surpreende todo mundo exatamente uma vez. Variáveis de ambiente no formato FORGEJO__section__KEY são gravadas nesse arquivo a cada inicialização, então você pode gerenciar toda a configuração a partir do arquivo do Compose — mas, se você editar o app.ini manualmente, uma variável de ambiente para a mesma chave vai sobrescrever a sua edição no próximo reinício. Escolha um dos dois caminhos e seja consistente.
Envie por SSH. Depois assine, para que o histórico não dependa do servidor.
Cole sua chave pública nas configurações de perfil, e depois ensine ao seu cliente onde a forja mora. Como o Git está em uma porta não padrão, uma entrada no seu arquivo de configuração de SSH vale os trinta segundos — ela mantém a porta fora de toda URL que você vai digitar dali em diante, e torna os remotos portáveis.
# ~/.ssh/config
Host git.example.com
Port 2222
User git
IdentityFile ~/.ssh/id_ed25519
# Then, in any repository:
git remote set-url origin git@git.example.com:you/project.git
git push -u origin main
ssh -T git@git.example.com # expect a greeting, not a shell
Esse último comando é o que te diz se o mapeamento de porta, o servidor SSH do contêiner e a sua chave estão todos de acordo. Uma saudação do Forgejo significa que toda a cadeia funciona. Um pedido de senha significa que você chegou, em vez disso, ao sshd administrativo do host, e o mapeamento de porta está errado.
Agora assine os seus commits. Essa é a etapa que a maioria dos guias de auto-hospedagem pula, e em uma forja auto-hospedada ela importa mais do que em uma plataforma. O campo de autor de um commit é texto livre — qualquer um que possa fazer push pode escrever o seu nome nele. Uma assinatura é prova de que uma chave específica fez um commit específico, e ela continua sendo prova mesmo depois que o servidor para o qual foi enviada tiver sido comprometido, migrado ou descartado. O Git consegue assinar com a chave SSH que você já tem, então não é mais preciso o desvio pelo PGP:
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
git config --global tag.gpgsign true
git commit --allow-empty -m "signed"
git log --show-signature -1
Depois adicione a mesma chave pública uma segunda vez na forja, como chave de assinatura, e não como chave de autenticação. O Forgejo verifica a assinatura no lado do servidor e mostra o commit como verificado; sem essa segunda entrada, ele não tem como conectar a assinatura à sua conta, e todo commit aparece como não verificado, mesmo estando assinado corretamente.
Chaves de implantação, para as máquinas que só leem. Um servidor de produção, uma máquina de build ou um espelho não precisam de uma conta de usuário na forja. Dê a eles uma chave de implantação em vez disso: um par de chaves adicionado a um único repositório, somente leitura a menos que você conceda escrita explicitamente, sem dar acesso a mais nada na instância. Revogá-la é apagar uma entrada. Esse é o hábito mais eficaz de todos para manter uma forja auto-hospedada organizada, porque contas de usuário acumulam permissões, e chaves de implantação não podem.
Migrar de uma plataforma é um bom momento para fazer tudo isso direito. O fluxo New Migration do Forgejo recebe a URL de um repositório e um token de acesso, e traz consigo branches, tags, issues, comentários, etiquetas, marcos, releases e pull requests. Rode um repositório primeiro para ver a cara do resultado, espere que os limites de taxa da API deixem os lotes grandes mais lentos, e mantenha a origem somente leitura por algumas semanas, em vez de apagá-la no mesmo dia.
Um runner de CI é uma máquina que roda código de estranhos. Posicione-o de acordo.
O Forgejo Actions lê arquivos de workflow no mesmo formato do GitHub Actions, a partir de .forgejo/workflows/, e os entrega a um programa separado chamado Forgejo Runner. O runner não é parte da forja e não precisa morar na mesma máquina — esse é todo o design por trás disso, e o motivo pelo qual esta etapa vem com um aviso, e não com um dar de ombros.
Ative o Actions na instância, e depois obtenha um token de registro no painel de administração — para a instância inteira, ou restrito a uma organização ou um repositório, o que é a opção padrão mais sensata quando você está em dúvida. O runner se conecta para fora, em direção à forja, então ele não precisa de nenhuma porta de entrada e pode ficar atrás de qualquer firewall que você quiser, inclusive em uma máquina sem endereço público.
# In the Compose environment of the forge, or app.ini:
# FORGEJO__actions__ENABLED: "true"
# Then: Site administration -> Actions -> Runners -> Create new runner
# On the runner machine:
forgejo-runner generate-config > config.yml
forgejo-runner register --no-interactive \
--instance https://git.example.com \
--token <REGISTRATION_TOKEN> \
--name runner-01 \
--labels docker:docker://node:22-bookworm,debian:docker://debian:12
forgejo-runner --config config.yml daemon
As etiquetas são o contrato entre o workflow e o runner: um job que pede runs-on: docker recebe um contêiner construído a partir da imagem do lado direito. Declare apenas as etiquetas que você realmente está preparado para atender, porque um job que pede uma etiqueta que ninguém oferece fica esperando para sempre, em vez de falhar, o que é uma coisa confusa de depurar no fim de um dia longo.
.forgejo/workflows/ci.yml
on: [push, pull_request]
jobs:
test:
runs-on: docker
container:
image: node:22-bookworm
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test
Um runner executa o que quer que o arquivo de workflow diga, e o arquivo de workflow chega em um pull request. Se qualquer pessoa fora da sua equipe pode abrir um, o runner pertence a uma máquina diferente da forja — um segundo Sentinel a $3.90 por mês, sem acesso ao diretório de dados da forja, sem credenciais de banco de dados e sem nada no seu ambiente que você se incomodaria de ver em um log de build. Na própria forja, exija aprovação antes de rodar workflows de contribuidores de primeira viagem. As duas falhas mais comuns são igualmente banais e igualmente caras: uma carga de mineração escondida em um pull request, e um segredo impresso por uma etapa que alguém adicionou de propósito.
Restrinja o escopo dos segredos da mesma forma. O Forgejo os guarda em nível de repositório, organização e instância; uma chave de implantação que só um repositório precisa deveria existir somente nesse repositório, para que um workflow comprometido em um projeto sem relação nenhuma não consiga lê-la. Prefira tokens de vida curta a tokens de vida longa sempre que o destino permitir, e nunca coloque um segredo em um arquivo de workflow — o arquivo de workflow está no repositório, e o repositório tem histórico.
Se os seus builds baixam as mesmas imagens-base a cada execução, coloque-as em cache. Um espelho local de registry ou um cache persistente no runner transforma um download de imagem de dois minutos em um de dois segundos, e é a atitude educada para com os registries upstream que você está sobrecarregando. E vale notar o padrão que todo esse design convida a seguir, e é o motivo pelo qual este guia fica ao lado do guia de runtime para agentes: o runner é um trabalhador de propósito geral que busca jobs na sua forja, então é também o lugar certo para manutenção agendada, pipelines de dados e cargas de trabalho de agentes.
Todo clone é um backup do código. Nada é um backup da conversa.
O Git é incomumente tolerante nesse ponto, e vale a pena ser preciso sobre o porquê. Todo clone é uma cópia completa do histórico, então, contanto que um desenvolvedor tenha uma cópia de trabalho atualizada, o código sobrevive ao servidor. Isso é genuinamente tranquilizador, e cobre exatamente uma das duas coisas que a sua forja guarda. A outra — issues, discussões de pull request, threads de revisão, releases, páginas de wiki, usuários, equipes, permissões, histórico de CI — existe só no banco de dados, ninguém tem um clone dela, e costuma ser a parte que levou três anos para se acumular.
Primeiro, um espelho push. Ele é gratuito e cuida de si mesmo. Em um repositório: Settings → Mirror settings → adicione um espelho push com a URL e um token de acesso para o destino. O Forgejo então replica cada branch e cada tag depois de todo push, para um segundo Forgejo, para o Codeberg, ou para uma plataforma que você decidiu manter como vitrine, e não como casa. Uma ressalva que vale a pena deixar bem clara: um espelho push republica o histórico fielmente, incluindo um segredo que você queria ter removido.
Depois um backup de verdade, que não é o arquivo embutido. O Forgejo vem com um comando forgejo dump que empacota configuração, repositórios, dados e um dump do banco de dados em um único zip. É uma coisa genuinamente útil para entregar a um mantenedor ao relatar um bug. Não é aquilo com que você deveria contar para voltar dos mortos: o SQL que ele produz tem problemas conhecidos de longa data na reimportação, e o arquivo reescreve todo o conjunto de repositórios a cada execução, o que, em uma instância real, significa um arquivo grande escrito do zero toda noite. Use o caminho chato e verificável em vez disso — faça o dump do banco de dados com a própria ferramenta do banco, e faça backup do diretório de dados como arquivos:
#!/bin/sh
# /usr/local/bin/forgejo-backup — run from a systemd timer, nightly
set -eu
cd /srv/forgejo
# 1. Consistent database dump, written next to the data.
docker compose exec -T db pg_dump -U forgejo -Fc forgejo > ./db.dump
# 2. Everything that matters, to an encrypted offsite repository.
restic backup ./data ./db.dump ./docker-compose.yml ./.env ./Caddyfile
# 3. Retention, then prove the repository is still readable.
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 12 --prune
restic check
Envie-o para uma máquina em uma jurisdição diferente da forja, por meio de um repositório somente para acréscimo, para que um comprometimento da forja não consiga apagar o próprio histórico — o guia de backups criptografados é a versão longa das três linhas acima, incluindo o timer do systemd e a restrição de chave somente para acréscimo.
Depois restaure tudo uma vez, de propósito. Peça uma VPS descartável, restaure o snapshot nela, suba o mesmo arquivo do Compose apontando para o diretório de dados restaurado, carregue o dump do banco de dados, aponte uma entrada no arquivo hosts para ela e faça login. Cronometre tudo isso. Esse número — não a mera existência do job de backup — é a sua posição real de recuperação, e todo mundo que já fez esse exercício encontrou pelo menos uma coisa faltando na lista de inclusão da primeira vez.
# restic keeps absolute paths, so the stack reappears under the target.
restic restore latest --target /srv/restore
cd /srv/restore/srv/forgejo
docker compose up -d db # empty volume: the entrypoint creates the database
docker compose exec -T db pg_restore -U forgejo -d forgejo \
--clean --if-exists --no-owner < db.dump
docker compose up -d # then sign in and walk the issue tracker
Atualizações, em resumo. Fixe a imagem em uma versão principal, em vez de :latest, para que um pull automático não te faça atravessar uma mudança que quebra compatibilidade enquanto você dorme. Leia as notas de lançamento antes de cruzar uma fronteira de versão principal, tire um snapshot antes, e lembre-se de que uma migração de banco de dados roda na primeira inicialização da nova versão — o que é uma porta sem volta. Atualização automática no estilo Watchtower é uma boa ideia para um site estático e uma péssima ideia para a máquina que guarda cada branch que você tem.
Quatro verificações antes de dar por encerrado. Saia da conta e confirme que a instância não oferece nenhuma forma de criar uma conta. A partir de um navegador sem sessão, confirme que um repositório privado fica invisível, e não apenas proibido. Confirme que o runner não consegue ler o diretório de dados da forja. E confirme que o último restic check passou — o job de backup que falha silenciosamente é aquele que todo mundo tem, até o dia em que precisa dele.
Você removeu uma parte envolvida. Você não removeu o prédio.
Auto-hospedar uma forja é uma mudança real em quem detém o seu trabalho, e não é invisibilidade. Quatro coisas continuam verdadeiras depois que tudo o que foi dito acima está feito, e conhecê-las é a diferença entre uma defesa de verdade e uma história que você conta para si mesmo.
O disco está no prédio de alguém. Um servidor alugado roda em hardware dentro de um datacenter com portas e equipe. A NordBastion não inspeciona as cargas de trabalho dos clientes, e o relatório de transparência e o warrant canary descrevem o que aconteceria se nos pedissem para fazer isso — mas a afirmação arquitetural é a honesta: o que muda é que não existe um índice, em nível de produto, dos seus repositórios, nenhum pipeline de análise sobre os seus commits e nenhuma política de privacidade que possa ser alterada. O que não muda é que a máquina é física, e ela não está na sua casa.
O hostname se torna público no momento em que o certificado é emitido. Todo certificado publicamente confiável é publicado em registros de transparência de certificados que qualquer um pode pesquisar, então git.yourproject.com é descobrível mesmo que você nunca crie um link para ele. Isso não é um vazamento do seu código, mas é um vazamento do fato de que o projeto existe e mais ou menos de quando ele começou. Use um subdomínio de um nome que você já publica, ou abra mão do certificado público por completo e acesse a forja via WireGuard ou um serviço onion.
Agora você está corrigindo uma aplicação web. A forja é uma página de login, um pipeline de renderização para marcação não confiável, um servidor SSH e um motor de CI, tudo acessível pela internet. Essa é uma superfície consideravelmente maior do que o repositório nu da etapa 01, e é uma superfície que você precisa manter atualizada. Assine os anúncios de lançamento, e trate um lançamento de segurança como uma tarefa para a mesma semana, não para o mesmo trimestre.
A federação ainda não chegou. O Forgejo está fazendo um trabalho real de federação entre forjas e publica o progresso disso, mas, até hoje, você não consegue abrir um pull request na instância de outra pessoa a partir da sua, do jeito que consegue em uma plataforma compartilhada. Na prática, isso significa que um contribuidor externo ainda precisa de uma conta na sua forja, ou você aceita patches por e-mail, ou você mantém um espelho público para contribuições ocasionais. Planeje-se para qual das três opções combina com o seu projeto, em vez de ser pego de surpresa por isso.
O que você realmente comprou, com precisão: o registro datado do que você constrói, com quem, e quando, deixa de ficar nas mãos de uma empresa cujo modelo de negócio inclui analisá-lo. Essa é uma afirmação estreita, e é uma afirmação real. Ela vale o que custa somente se a etapa 06 estiver feita — uma forja auto-hospedada sem uma restauração testada não é uma melhoria de privacidade, é um único ponto de falha com uma marca melhor.
A forja é pequena. O CI não é.
| Nível | Mensal | Recursos | O que ela roda com folga |
|---|---|---|---|
| Sentinel | $3.90 | 2 vCPU · 4 GB · 120 GB | Forgejo + PostgreSQL para uma pessoa ou uma equipe pequena. O começo certo. |
| Um segundo Sentinel | $3.90 | O runner, na própria máquina dele | O isolamento que realmente importa, pelo preço de um café |
| Garrison | $7.90 | 4 vCPU · 8 GB · 240 GB | Uma forja para dez pessoas, um registry de contêineres e artefatos |
| Ravelin | $16.90 | 8 vCPU · 16 GB · 480 GB | CI ocupado, repositórios grandes, vários jobs em paralelo |
Nada nesta tabela é dimensionado para o Git. Forgejo e PostgreSQL juntos ficam ociosos em algumas centenas de megabytes; o que enche uma máquina é o CI — camadas de contêiner, caches de build, artefatos — e conteúdo de repositório que não é código-fonte. Dimensione pelo disco primeiro, pelos núcleos em segundo lugar, e pela memória por último. Duas máquinas pequenas vencem uma grande aqui, porque a segunda é uma fronteira de segurança, não de capacidade.
Seis formas de isso dar errado. Duas delas custam dinheiro.
Registro aberto, e uma forja cheia de estranhos
O certificado aparece nos registros de transparência, os scanners chegam em poucos dias, e uma instância com cadastros habilitados se enche de contas spam e repositórios spam. Defina DISABLE_REGISTRATION antes da primeira inicialização, não depois do primeiro incidente.
O runner que minera criptomoeda para outra pessoa
Um CI que roda workflows de qualquer pull request é computação de graça para quem encontrar. Exija aprovação para contribuidores de primeira viagem, mantenha o runner na sua própria máquina, e leia a duração dos jobs de vez em quando.
ROOT_URL errado, e tudo funciona pela metade
As URLs de clone apontam para o host errado, os webhooks disparam para o endereço errado, os callbacks de OAuth falham e os avatares quebram. A interface parece normal, então ninguém suspeita de uma única linha. Defina DOMAIN, ROOT_URL, SSH_DOMAIN e SSH_PORT juntos, e confira uma URL de clone na interface.
Dois servidores SSH, uma porta
O contêiner quer a porta 22, e o host já está usando ela, então o mapeamento cai silenciosamente em algum lugar inesperado, e o git push pede uma senha. Mude o SSH administrativo primeiro, publique o contêiner em uma porta só dele, e verifique com ssh -T.
Um dump que nunca foi restaurado
O arquivo embutido é um artefato de suporte, não um caminho de recuperação testado, e o seu SQL tem problemas conhecidos de reimportação. Faça o dump do banco de dados com pg_dump, faça backup do diretório de dados como arquivos, e restaure tudo em uma máquina descartável uma vez.
O disco enche de camadas de build, não de código
Código-fonte é minúsculo; camadas de contêiner, artefatos e caches de CI não são. Uma máquina de 120 GB com um pipeline entusiasmado fica cheia em semanas. Faça a limpeza de imagens em um agendamento, limite a retenção de artefatos, e observe o disco, não o processador.
Perguntas, respondidas.
Onze perguntas que surgem ao construir isso — começando pelas duas que decidem se você deveria sequer construir isso.
Forgejo ou Gitea — qual dos dois eu deveria realmente instalar?
Forgejo, para a maioria das pessoas, e o motivo é governança, não recursos. Os dois projetos compartilham um ancestral comum e ainda parecem quase idênticos no uso do dia a dia: mesmo binário em Go, mesma pegada leve, mesma interface web, CI comparável. O que difere é quem está no comando. O Forgejo é mantido pela Codeberg e.V., uma associação registrada sem fins lucrativos em Berlim, e se tornou um hard fork no início de 2024; publica tudo como software livre e não exige que os contribuidores cedam os direitos autorais. O Gitea é desenvolvido por uma empresa com fins lucrativos que é dona da marca e do domínio, exige cessão de direitos autorais dos contribuidores e opera um modelo open-core, com recursos pagos ao redor da base gratuita. Nenhum desses fatos torna o Gitea um software ruim. Mas o objetivo de tirar seus repositórios de uma plataforma é justamente parar de depender do roadmap de uma empresa, então escolher o fork cuja mantenedora é uma organização sem fins lucrativos é a decisão que combina com o motivo de você estar aqui. Uma observação prática caso você troque depois: o Forgejo pode ser instalado sobre um diretório de dados existente do Gitea, mas o caminho inverso não é suportado.
Eu realmente preciso de uma forja? Um repositório nu via SSH não basta?
Para uma pessoa só, com um laptop e o hábito de escrever boas mensagens de commit, um repositório nu é realmente suficiente, e é a coisa mais robusta deste guia: git init --bare no servidor, uma conta SSH comum, e não há aplicação web para corrigir, nenhum banco de dados para fazer backup e nenhuma superfície de ataque além do sshd. Nada nessa configuração jamais vai chegar ao fim de vida. Você precisa de uma forja no momento em que uma segunda pessoa entra em cena, ou no momento em que você quiser qualquer uma das quatro coisas que um repositório nu simplesmente não oferece — um histórico navegável pelo navegador, pull requests com revisão, um rastreador de issues que vive ao lado do código, e controle de acesso por repositório. Isso não é um pequeno passo à frente; é a diferença entre armazenamento e colaboração. Seja honesto sobre qual dos dois problemas você está resolvendo, porque uma forja é um serviço que você passa a operar, e um repositório nu é apenas um diretório.
Posso migrar meus repositórios do GitHub, incluindo issues e pull requests?
Sim, e essa é uma das melhores histórias de migração na auto-hospedagem. A ferramenta de migração embutida do Forgejo recebe a URL de um repositório e um token de acesso pessoal, e traz muito mais do que o código: branches e tags, issues com seus comentários, pull requests, etiquetas, marcos e releases. Dê a ela um token com escopo de leitura, rode primeiro um repositório para ver como fica o resultado, e depois passe para o resto. Três coisas a esperar. Migrações grandes ou numerosas vão esbarrar nos limites de taxa da API da plataforma de origem e simplesmente vão demorar mais — comece-as e deixe-as rodando. Os anexos das issues são buscados pela rede, então uma instância atrás de um firewall de saída restritivo vai gerar issues com imagens quebradas. E as discussões de revisão nem sempre caem exatamente onde estavam, porque os dois modelos de dados não são idênticos. Planeje manter os repositórios de origem somente leitura por algumas semanas, em vez de apagá-los na mesma tarde.
Uma forja auto-hospedada é realmente mais privada, ou só diferente?
É genuinamente diferente em um aspecto específico e importante: ninguém além de você guarda o índice. Em uma plataforma, o repositório privado é privado para os outros usuários — ele não é privado para a plataforma, que o armazena, o escaneia, calcula um grafo de dependências a partir dele, e pode ser obrigada por um tribunal, na sua própria jurisdição, a entregá-lo. Esse índice é incomumente revelador mesmo quando o código é banal: os horários dos commits revelam quando você trabalha, os coautores revelam quem é sua equipe, os nomes de branch revelam produtos ainda não lançados, e o primeiro commit data um projeto que ainda não foi anunciado. Mudar isso para o seu próprio servidor remove a plataforma inteiramente dessa equação. O que isso não faz é fazer a máquina desaparecer. Você continua alugando hardware em um datacenter, o disco continua fisicamente acessível pelas pessoas donas do prédio, e o hostname que você escolheu continua nos registros de transparência de certificados. O resumo honesto: você trocou uma empresa com uma política de privacidade contratual por um servidor cuja única política é a sua configuração, em uma jurisdição que você escolheu deliberadamente — e a conta que paga por ela não precisa levar o seu nome.
De que tamanho o VPS precisa ser?
Menor do que a documentação do GitLab treinou todo mundo a esperar. Forgejo e Gitea são binários únicos em Go que ficam ociosos em algumas centenas de megabytes de RAM; acrescente o PostgreSQL e um proxy reverso, e a forja de um desenvolvedor solo cabe confortavelmente em um Sentinel — 2 vCPU, 4 GB, 120 GB NVMe, $3.90 por mês — com espaço de sobra. A pressão vem de três outros lugares, e cada um deles merece ser dimensionado deliberadamente. O CI é o maior deles: um runner compilando contêineres vai usar cada núcleo que você der a ele e encher o disco de camadas. O tamanho do repositório é o segundo: assets de jogos, notebooks e dependências vendorizadas crescem muito mais rápido do que o código-fonte. Artefatos e um registry de contêineres são o terceiro. Uma regra que se provou boa na prática — coloque a forja em um Sentinel, coloque o runner em um segundo Sentinel só dele, e só suba de plano quando o disco mandar, não quando o processador mandar.
O Forgejo Actions é realmente compatível com meus workflows do GitHub Actions?
Compatível o bastante para que a maioria dos workflows diretos rode sem alterações, e não tão compatível a ponto de você poder migrar um pipeline complexo sem lê-lo primeiro. O formato do arquivo de workflow é o mesmo, ele vive em .forgejo/workflows/ (e .github/workflows/ também é lido), e os blocos de construção comuns — checkout, configuração da linguagem, cache, builds em matriz — se comportam como o esperado, porque o runner os executa em contêineres da mesma forma. O que quebra é qualquer coisa que pressupõe o GitHub como plataforma, e não as Actions como formato: actions do marketplace fixadas em URLs de github.com que a sua instância precisa ter permissão para buscar, actions que chamam a API REST do GitHub para metadados de release ou de PR, imagens de runner hospedadas pelo GitHub com um toolchain pré-instalado bem específico, e autenticação em nuvem baseada em OIDC. O padrão de migração que funciona: comece com um workflow que só compila e testa, deixe esse ficar verde, e depois adicione de volta as etapas de implantação uma de cada vez.
O que acontece com o meu código se a VPS morrer?
O código em si é a única parte que fica bem, e essa é a vantagem discreta do Git sobre quase qualquer outro serviço auto-hospedado: todo clone é uma cópia completa do histórico, então, contanto que um desenvolvedor tenha uma cópia de trabalho atualizada, o repositório não se perde. O que morre com o servidor é tudo o que está ao redor do código — issues, discussões de pull request, comentários de revisão, releases, wiki, usuários, regras de acesso, histórico de CI. Esse material existe só no banco de dados, ninguém tem um clone dele, e costuma ser a parte que levou anos para se acumular. Então trate os dois de formas diferentes. Para o código, configure um espelho push para um segundo local e você já tem proteção externa, contínua e automática, sem esforço nenhum. Para os metadados, você precisa de um backup de verdade: um dump do PostgreSQL mais o diretório de dados, criptografado e enviado a uma máquina em outra jurisdição com um agendamento periódico. A etapa 06 cobre os dois casos, e o exercício de restauração no final é o que transforma isso de uma intenção em uma garantia.
Posso manter o GitHub como espelho em vez de abandoná-lo de vez?
Sim, e para muitos projetos essa é a resposta certa, não uma solução de compromisso. O Forgejo suporta espelhos push, então a sua instância é a origem contra a qual a sua equipe trabalha, e cada push é replicado automaticamente para um repositório no GitHub, no Codeberg ou em um segundo Forgejo. A visibilidade pública, os contribuidores ocasionais, os registries de pacotes que esperam uma URL do GitHub e o link do rastreador de issues em um README antigo continuam funcionando, enquanto a fonte da verdade — e os repositórios privados, e os logs de CI, e o histórico de issues de tudo que você não publica — fica na sua máquina. Também funciona na direção oposta: espelhos pull mantêm uma cópia local e sempre atualizada de projetos upstream dos quais você depende, o que vale a pena fazer para qualquer coisa que faria falta de verdade se fosse apagada amanhã. A única coisa sobre a qual você precisa ser deliberado é o que está espelhando: um espelho push de um repositório cujo histórico contém um segredo que você queria ter removido republica esse segredo, imediata e automaticamente.
Como dou acesso a um contratado em apenas um repositório, sem dar acesso ao resto?
Use a camada que combina com o tempo que o acesso deve durar. Para uma pessoa que vai ler, fazer commits e abrir pull requests, crie uma conta de usuário e adicione-a como colaboradora exatamente nos repositórios de que ela precisa, com um nível de permissão explícito — leitura, escrita ou admin — em vez de colocá-la em uma equipe da organização que silenciosamente concede mais do que isso. Para um sistema automatizado que só precisa buscar dados — um alvo de implantação, uma máquina de build, um espelho — use uma chave de implantação somente leitura em vez de uma conta de usuário: ela é restrita a um único repositório, não concede mais nada além disso, e revogá-la é apagar uma linha. Para scripts que conversam com a API, emita um token de acesso com escopo limitado em vez de entregar uma senha. Dois hábitos sustentam tudo isso: mantenha o registro fechado, para que as únicas contas existentes sejam as que você criou, e marque uma data no seu calendário quando conceder acesso temporário, porque nada em nenhuma forja jamais vai te lembrar que um contrato acabou.
A forja precisa de um nome de domínio, e isso vaza a minha identidade?
Ela precisa de um hostname para a interface web, porque certificados TLS são emitidos para nomes, e nenhum navegador deveria confiar em um endereço IP aqui. Ela não precisa que esse nome seja rastreável até você. Três coisas para tratar com cuidado. Registre o domínio em algum lugar que não exija a identidade que você protegeu com tanto cuidado em outros lugares, e mantenha a privacidade do WHOIS ativada — o registrador é uma decisão de confiança separada da do host, e é a que todo mundo esquece. Espere que o hostname se torne público no momento em que o certificado for emitido, porque todo certificado publicamente confiável é publicado em registros de transparência de certificados que qualquer um pode pesquisar; um wildcard em um domínio que você já usa evita adicionar um nome novo e pesquisável. E se você preferir não ter nome público nenhum, a alternativa é real: coloque a forja em um serviço onion do Tor ou acessível somente via WireGuard, abra mão do certificado público por completo, e aceite que a conveniência de abrir um navegador em qualquer lugar vai junto.
A NordBastion se importa se eu rodar uma forja Git e runners de CI em uma VPS?
De jeito nenhum — uma forja e seus runners são uma carga de trabalho comum, e é uma das coisas que mais vemos nossos clientes construírem. A política de uso aceitável não tem nada a dizer sobre hospedar código-fonte ou compilá-lo. Duas notas operacionais, não de política. Primeiro, um runner de CI que aceita workflows de estranhos é um serviço de computação gratuito para quem o encontrar: o abuso padrão é mineração de criptomoeda dentro de um pull request, e ele vai ser encontrado poucos dias depois de ficar aberto. Mantenha o registro fechado, e exija aprovação antes de rodar workflows de qualquer pessoa que ainda não seja colaboradora. Segundo, se os seus builds baixam imagens de contêiner grandes a cada execução, isso é tráfego normal e está incluído — mas vale a pena colocá-las em cache localmente de qualquer forma, porque isso deixa o seu pipeline mais rápido e é a atitude educada com os registries upstream dos quais você depende.
Uma forja sua, numa conta que não é o seu nome. Sem KYC, pago em cripto.
Sentinel — 2 vCPU e 4 GB por $3.90 por mês, em Helsinki, Stockholm, Oslo ou Reykjavík. Suficiente para o Forgejo, o PostgreSQL e o proxy reverso, com espaço de sobra para os repositórios que você vinha adiando mover. Acrescente um segundo para o runner. Sem e-mail no cadastro, sem documento de identidade, e sem índice em nível de produto de nada que você enviar.
Última revisão · 2026-10-12 · Fontes · Documentação do administrador do Forgejo (instalação, Actions, linha de comando), a comparação do Forgejo com o Gitea, páginas de governança da Codeberg e.V., documentação do PostgreSQL e do Caddy, git-config(1) · Cadência · anualmente
Anonymous VPS hosting in 2026 — the cluster.
This guide is one spoke of a larger series. The pillar walks the three privacy layers end to end — the sibling spokes below dive into the specifics.
Three independent layers — signup, payment, network — explained, legal context included, common mistakes flagged.
Bitwarden-compatible password vault under your own control.
Files, calendar, contacts, photos — owned, not rented.
Postfix, Dovecot, SPF/DKIM/DMARC — deliverability included.
A meta search engine that does not log you — because you own it.