Servidor Git auto-hospedado: Forgejo, Gitea ou cgit
Compare 4 formas de hospedar Git por RAM: repositorios bare via SSH, cgit, Forgejo ou Gitea e GitLab. Saiba o que cabe em um VPS de 1 GB.
Qual servidor Git auto-hospedado deve executar
Um servidor Git auto-hospedado não é um único produto. A RAM (memória de acesso aleatório) do seu VPS determina qual versão pode executar. O Git não precisa do seu próprio daemon: um repositório bare e uma conta SSH (secure shell) já formam um servidor funcional na menor máquina que pode alugar. Tudo o que estiver acima desse nível é uma aplicação web que está a escolher executar junto dele. Cada nível adicional consome memória que um VPS pequeno pode não ter.
Existem quatro níveis. Um repositório bare através de SSH, sem nenhum serviço novo em escuta. cgit, uma interface web rápida e apenas de leitura, sem base de dados. Forgejo ou Gitea, uma forge completa com contas, issues e pull requests, em algumas centenas de megabytes. GitLab, que espera um servidor muitas vezes maior do que os restantes.
Decida com base no trabalho que precisa de realizar. Depois compare o valor de memória com o plano que está a pagar.
Quanta RAM cada opção realmente precisa
Apenas dois destes projetos publicam um valor de hardware. Trate esse valor como um mínimo, não como uma garantia, e meça a sua própria instância depois de estar em execução, com systemd-cgtop ou ps -o rss= -C forgejo.
The data behind this chart
[
{
"label": "Gitea, small team",
"ram_gb": 1
},
{
"label": "GitLab, memory constrained",
"ram_gb": 8
},
{
"label": "GitLab, single node baseline",
"ram_gb": 16
}
]O Gitea documenta 1 GB de RAM com 2 núcleos de CPU como normalmente suficientes para equipas e projetos pequenos, e indica um Raspberry Pi 3 como suficiente para cargas de trabalho pequenas. O GitLab documenta 16 GB como a base para uma instalação de nó único e 8 GB como o limite inferior do que a própria página chama de ambiente limitado em memória. O Forgejo não publica qualquer requisito de hardware. É um fork do Gitea e comporta-se da mesma forma, por isso o valor do Gitea é a orientação publicada mais próxima de que dispõe.
Num VPS com 1 GB, isto significa que repositórios bare e o cgit funcionam com memória de sobra, porque nenhum dos dois executa um serviço residente. O Forgejo ou o Gitea arrancam e servem uma equipa pequena com SQLite, mas estará no mínimo documentado. Por isso, mantenha o PostgreSQL e o runner de CI (integração contínua) desligados nesse servidor. Se a interface Web desaparecer sem apresentar um erro, execute sudo dmesg -T | grep -i oom e procure uma linha como Out of memory: Killed process 1181 (forgejo). Isso significa que o kernel a terminou através do mecanismo de eliminação por falta de memória. O GitLab num servidor com 1 GB não é um problema de afinação. Não irá funcionar.
Nível 0: um repositório bare sobre SSH
O Git não tem um daemon de rede que precise de iniciar. git push sobre SSH executa git-receive-pack no sistema remoto como um processo Unix comum. Por isso, qualquer conta que consiga alcançar com uma chave já é um remote Git. Crie uma conta para os repositórios. Mantenha os repositórios fora do diretório home dessa conta, porque no Ubuntu 24.04 um novo diretório home tem o modo 0750 e uma interface web adicionada mais tarde não conseguirá aceder ao seu conteúdo.
sudo adduser --system --shell /bin/bash --gecos 'Git Version Control' \
--group --disabled-password --home /home/git git
sudo install -d -m 0755 -o git -g git /srv/git
sudo -u git git init --bare /srv/git/project.git--bare cria um repositório sem working copy, que é o formato usado pelo servidor. O push para um repositório que tem uma working copy é recusado com refusing to update checked out branch: refs/heads/main. Este é o erro mais comum neste nível.
Agora atribua uma chave à conta e faça o clone.
sudo -u git install -d -m 700 /home/git/.ssh
sudo -u git tee -a /home/git/.ssh/authorized_keys <<'EOF'
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexamplekeyhere alice@laptop
EOF
sudo -u git chmod 600 /home/git/.ssh/authorized_keysgit remote add origin git@vps.example.com:/srv/git/project.git
git push -u origin mainUm primeiro push bem-sucedido termina com * [new branch] main -> main. Se terminar com git@vps.example.com: Permission denied (publickey), a autenticação nunca foi concluída. Nesse caso, consulte o log do servidor com sudo journalctl -u ssh -n 20. Uma linha com Authentication refused: bad ownership or modes for file /home/git/.ssh/authorized_keys significa que o modo do ficheiro está incorreto, porque o sshd ignora um ficheiro de chaves que possa ser alterado por outros utilizadores.
Depois, retire a shell da conta.
command -v git-shell | sudo tee -a /etc/shells
sudo chsh -s "$(command -v git-shell)" gitgit-shell aceita apenas os poucos comandos que o Git envia através de SSH. Por isso, uma sessão interativa termina agora com uma mensagem, em vez de apresentar um prompt:
fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.Este é o servidor completo. Não existe uma base de dados nem um processo web para atualizar. O que fica de fora é tudo o que uma forge oferece: não há navegação, issue tracker, pull requests nem permissões por utilizador. Todas as chaves nesse ficheiro podem ler e escrever em todos os repositórios que o utilizador git possui.
Camada 1: cgit fornece uma interface web sem uma base de dados
cgit é um programa CGI (common gateway interface) escrito em C. O servidor web executa-o uma vez por pedido, lê os repositórios diretamente do disco e não guarda estado próprio. O Ubuntu 24.04 inclui-o no componente universe.
sudo apt update
sudo apt install -y cgit fcgiwrap nginx
sudo install -d -o www-data -g www-data /var/cache/cgitIndique o diretório dos repositórios em /etc/cgitrc:
root-title=Git on example.com
css=/cgit.css
logo=/cgit.png
cache-size=1000
cache-root=/var/cache/cgit
snapshots=tar.gz zip
scan-path=/srv/gitscan-path percorre esse diretório e lista todos os repositórios encontrados. Assim, um novo repositório bare aparece sem configuração adicional. cache-size é o número de páginas em cache. O armazenamento em cache fica desativado enquanto esse valor for zero. Leia o conteúdo que o pacote já colocou em /etc/cgitrc antes de adicionar linhas, porque os pacotes Debian e Ubuntu fornecem alguns valores predefinidos.
Cada entrada mostra a primeira linha do ficheiro description do repositório. Por isso, um repositório bare novo aparece como Unnamed repository; edit this file 'description' to name the repository.. Corrija esse valor uma vez por repositório:
echo 'Project X, internal tooling' | sudo -u git tee /srv/git/project.git/descriptionO ficheiro do site nginx e como o verificar
server {
listen 80;
server_name git.example.com;
root /usr/share/cgit;
try_files $uri @cgit;
location @cgit {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /usr/lib/cgit/cgit.cgi;
fastcgi_param PATH_INFO $uri;
fastcgi_param QUERY_STRING $args;
fastcgi_param HTTP_HOST $server_name;
fastcgi_pass unix:/run/fcgiwrap.socket;
}
}sudo systemctl enable --now fcgiwrap.socket
sudo nginx -t && sudo systemctl reload nginx
systemctl show fcgiwrap.socket -p Listenroot /usr/share/cgit serve cgit.css e cgit.png como ficheiros simples, e try_files encaminha tudo o resto para o CGI em /usr/lib/cgit/cgit.cgi. Uma página 502, com connect() to unix:/run/fcgiwrap.socket failed (2: No such file or directory) em /var/log/nginx/error.log, significa que a unidade do socket não está em execução ou está a escutar noutro caminho. A linha systemctl show mostra o caminho que está efetivamente a utilizar.
Há dois limites que deve conhecer antes de o utilizar. cgit é apenas de leitura e não tem autenticação. Por isso, tudo em scan-path é público: mantenha os repositórios privados fora desse diretório ou coloque autenticação HTTP basic à frente de todo o site. Além disso, o CGI é executado com o utilizador do servidor web. Esse utilizador precisa de atravessar /srv/git e de ler cada repositório. Um diretório que não consiga abrir aparece como um índice vazio, e não como um erro.
Nível 2: Forgejo ou Gitea para issues e pull requests
Forgejo e Gitea seguem a mesma ideia: um único binário Go que disponibiliza uma forge Web com utilizadores, organizações, issues, pull requests, releases, um registo de pacotes e um sistema de CI integrado. O binário e o SQLite são toda a instalação, razão pela qual cabem em hardware onde o GitLab nem sequer funciona. O ficheiro Compose abaixo é o que consta na documentação do Forgejo, com a tag da imagem indicada em agosto de 2026.
networks:
forgejo:
external: false
services:
server:
image: codeberg.org/forgejo/forgejo:16
container_name: forgejo
environment:
- USER_UID=1000
- USER_GID=1000
restart: always
networks:
- forgejo
volumes:
- ./forgejo:/data
- /etc/localtime:/etc/localtime:ro
ports:
- '3000:3000'
- '222:22'docker compose up -d
docker compose ps
curl -sI http://127.0.0.1:3000 | head -1A linha curl deve apresentar uma linha de estado HTTP. Antes de concluir a configuração inicial, pode apresentar um redirecionamento para /install, o que continua a significar que o serviço está ativo. Se o contentor terminar, a causa habitual são as permissões: o diretório ./forgejo tem de pertencer ao UID (ID de utilizador) indicado em USER_UID, caso contrário o processo não consegue escrever no seu próprio diretório de dados. Docker Compose num VPS explica detalhadamente essa disposição de ficheiros e a regra de propriedade dos volumes.
Duas respostas na página de configuração determinam se os URLs de clone funcionam. A porta SSH tem de ser 222, porque o ficheiro Compose mapeia a porta 222 do anfitrião para a porta 22 do contentor, e o domínio tem de ser o nome que as pessoas vão realmente escrever. Se uma delas estiver errada, todas as páginas de repositórios apresentam um comando de clone que falha para qualquer pessoa que o copie. Depois, ambas ficam na secção [server] de app.ini, como SSH_PORT, SSH_DOMAIN e ROOT_URL.
Numa instância pública, publique a porta Web apenas no endereço de loopback ('127.0.0.1:3000:3000') e coloque o nginx à frente para tratar do TLS (segurança da camada de transporte). O Gitea é instalado da mesma forma a partir da imagem gitea/gitea, ou como um único binário com uma unidade systemd e um único app.ini. A versão estável atual é a 1.27.1, em agosto de 2026.
Mantenha o SQLite enquanto puder. A instância fica limitada a um processo e um ficheiro, e sobrevive a um reboot sem outro serviço para supervisionar. O PostgreSQL justifica o custo quando várias pessoas escrevem em simultâneo, porque o SQLite serializa as escritas e as execuções longas de CI escrevem constantemente. Ambos os projetos podem migrar posteriormente uma instância existente para PostgreSQL, portanto não é uma decisão irreversível.
Forgejo ou Gitea: o que realmente difere
A linhagem é partilhada. O Gitea foi criado a partir do Gogs em 2016. No final de 2022, o controlo do domínio e da marca registada Gitea passou para uma empresa, a Gitea Ltd, e vários responsáveis pela manutenção, juntamente com a Codeberg, iniciaram o Forgejo. O Forgejo é publicado pela Codeberg e.V., uma associação sem fins lucrativos registada na Alemanha, e mudou da licença MIT para a GPLv3 (GNU general public license version 3) em 2024. O Gitea continua sob a licença MIT e é desenvolvido com apoio comercial.
No dia a dia, os conjuntos de funcionalidades são semelhantes. O caminho de migração entre os dois não é. O Forgejo v10.0, de janeiro de 2025, foi a última versão que podia utilizar diretamente uma base de dados do Gitea, e apenas a partir do Gitea v1.22 ou anterior. Em agosto de 2026, o Gitea está na versão 1.27.1. Por isso, uma instância atual do Gitea não tem uma migração suportada para o Forgejo no mesmo sistema. Escolha um deles antes de o preencher com dados e trate qualquer migração posterior como uma exportação seguida de uma nova importação.
Uma regra simples para escolher. Se a governação for importante para si, ou se quiser que o projeto continue numa organização sem fins lucrativos, utilize o Forgejo. Se quiser uma base de instalações maior e uma opção de suporte comercial, utilize o Gitea. Ambos são mantidos de forma aberta e lançam versões com frequência: o Forgejo publica uma versão estável a cada três meses e uma versão LTS (long term support) por ano. Em agosto de 2026, a versão atual é a v16.0.2 e a versão LTS é a v15.0.6.
Camada 3: quanto o GitLab custa antes de fazer qualquer coisa
O GitLab CE é uma classe diferente de software. Uma instância é um conjunto de serviços que trabalham em conjunto: Puma para a aplicação web, Sidekiq para tarefas em segundo plano, PostgreSQL, Redis, Gitaly para acesso aos repositórios e nginx na frente. O pacote Omnibus instala-os em conjunto, o que simplifica a instalação e eleva o consumo mínimo de memória.
A página de requisitos do GitLab documenta 16 GB de RAM e 8 vCPU como base para uma instalação de nó único, indicando 8 GB como o limite inferior num ambiente com memória limitada. A mesma página recomenda desativar o swap, porque a utilização de swap sob carga degrada gravemente a instância. Estes são os valores publicados em agosto de 2026 e têm aumentado ao longo dos anos. Consulte novamente a página antes de dimensionar um servidor.
Este orçamento proporciona funcionalidades concretas: um registo de contentores, um registo de pacotes, permissões granulares, funcionalidades de conformidade e auditoria e CI testado em grande escala. Se ninguém na sua equipa conseguir indicar algo dessa lista de que precise neste trimestre, está a pagar por um VPS maior sem obter qualquer benefício.
O modelo de acesso SSH: um utilizador git e várias chaves
Cada nível autentica da mesma forma. Existe uma conta Unix chamada git, e todas as chaves públicas são colocadas no ~/.ssh/authorized_keys dessa conta. A autenticação é feita pela chave. A autorização é definida pelas opções que escreve antes da chave na mesma linha.
Uma linha de chave simples concede ao titular tudo o que essa conta pode fazer. Um comando forçado limita o acesso ao Git:
restrict,command="git-shell -c \"$SSH_ORIGINAL_COMMAND\"" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexamplekeyhere alice@laptoprestrict, disponível desde o OpenSSH 7.2, desativa o encaminhamento de portas, o encaminhamento do agente, o X11 e a alocação de PTY (pseudo terminal) numa única palavra. command= substitui o pedido feito pelo cliente pelo comando indicado, e o Git continua a funcionar porque envia o pedido em $SSH_ORIGINAL_COMMAND.
Uma forge escreve esse ficheiro por si, e essa é a verdadeira diferença entre o nível 0 e o nível 2. Forgejo e Gitea reescrevem authorized_keys com uma linha por cada chave registada, cada uma contendo um comando forçado que identifica a chave pelo seu ID na base de dados:
command="/usr/local/bin/forgejo --config=/etc/forgejo/app.ini serv key-3",no-port-forwarding,no-x11-forwarding,no-agent-forwarding,no-pty ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIexamplekeyhere aliceEsse comando forçado transforma uma conta Unix partilhada em permissões por utilizador: key-3 informa a forge sobre o utilizador que está a ligar-se, e esta verifica esse utilizador contra o repositório antes de qualquer objeto ser transferido. Não edite manualmente esse ficheiro num sistema gerido pela forge, porque ele é reescrito a partir da base de dados e a sua linha desaparece. As deploy keys usam o mesmo mecanismo: uma deploy key é uma chave SSH normal registada num único repositório, geralmente apenas para leitura, com a verificação feita pela forge em vez do sshd.
Dois hábitos são mais importantes do que qualquer uma das configurações anteriores. Emita uma chave por pessoa ou por máquina, nunca uma chave partilhada, porque revogar uma chave partilhada implica substituí-la para todos ao mesmo tempo. Remova as chaves no dia em que alguém sai, porque uma chave antiga nesse ficheiro é um login permanente que ninguém está a monitorizar. Boa gestão de chaves SSH num servidor aborda os tipos de chave e as passphrases, e tudo isso se aplica aqui sem alterações. Se o sistema for novo, os primeiros dez minutos num VPS novo é o procedimento correto antes de colocar repositórios nele.
Posso executar GitHub Actions no meu próprio servidor Git?
Pode executar workflows escritos na sintaxe do GitHub Actions. Não pode executar o GitHub. O Forgejo Actions vem ativado por predefinição desde o Forgejo v1.21 e lê os ficheiros de workflow em .forgejo/workflows de cada repositório. O Gitea Actions funciona da mesma forma e lê .gitea/workflows. Ambos precisam de um segundo programa, o runner, instalado e registado na sua instância com um token obtido nas definições de administrador. Muitas actions publicadas funcionam sem alterações; tudo o que chamar a API do GitHub ou esperar uma infraestrutura alojada pelo GitHub não funciona.
Tenha em conta duas consequências. O runner inicia um contentor para cada job, por isso precisa de um motor de contentores e de um orçamento de memória próprio. É por isso que não deve ficar na mesma máquina de 1 GB que o forge. Além disso, o runner executa tudo o que um ficheiro de workflow indicar. A documentação do Forgejo afirma isso de forma explícita: o runner executa código remotamente. Sempre que possível, coloque-o num host próprio. No mínimo, use um utilizador sem privilégios próprio e um token de registo limitado a um repositório.
Se os seus repositórios continuarem no GitHub e apenas quiser executar os workloads em hardware que controla, essa é uma configuração diferente, com passos diferentes: um runner self-hosted do GitHub Actions associa-se a um repositório do GitHub e não precisa de nada disto. Se ainda estiver a avaliar o custo de sair, o que o GitHub oferece realmente separa o alojamento Git da rede que o envolve.
Backups: repositories são apenas metade do estado
Um repositório bare é um diretório, portanto copiá-lo copia tudo o que contém. Um clone mirror de outra máquina é uma cópia de segurança real e é atualizado no local:
git clone --mirror git@vps.example.com:/srv/git/project.git
cd project.git && git remote updateIsto obtém todas as refs e todos os objetos. Não obtém os hooks do servidor nem o ficheiro description. Se usar hooks, mantenha também uma cópia ao nível dos ficheiros do diretório.
Uma forge mantém issues, pull requests, utilizadores, chaves e permissões na base de dados. Uma cópia apenas dos repositórios elimina todos esses dados. Ambos os projetos disponibilizam um comando dump que grava a base de dados, os repositórios, a configuração e os anexos num único arquivo:
sudo -u git forgejo dump -c /etc/forgejo/app.ini -f /var/backups/forgejo-dump.zipNo Docker, o mesmo comando é executado dentro do contentor. O caminho da configuração depende da imagem, por isso confirme-o antes de executar o comando:
docker compose exec server ls /data/gitea/conf
docker compose exec -u git server forgejo dump -c /data/gitea/conf/app.iniExecute-o como o utilizador proprietário dos dados e grave o arquivo num diretório onde esse utilizador tenha permissões de escrita. Depois, copie o arquivo para fora do servidor. Uma cópia de segurança que só existe na máquina que está a ser protegida não é uma cópia de segurança. Restaurar é a etapa que muitas pessoas ignoram: descompacte agora um dump numa máquina sobressalente, para aprender o procedimento num momento tranquilo, em vez de o fazer durante uma interrupção.
Escolha por cenário
Uma pessoa com um laptop e um VPS, sem necessidade de navegação: repositórios bare por SSH. Não há nenhum serviço adicional em execução nem nada para atualizar.
O mesmo cenário, mas com a necessidade de ler código num navegador e enviar links para ele: adicione o cgit. Continua sem base de dados e sem processos residentes.
Uma equipa que revê o código uns dos outros e acompanha problemas: Forgejo ou Gitea, com 2 GB de RAM ou mais. Mova o runner de CI para uma segunda máquina quando os jobs se tornarem relevantes.
Uma organização que precisa de um registry de containers e de trilhos de auditoria, com 16 GB disponíveis para o servidor: GitLab. Abaixo desse orçamento, não o inicie.
Subir pelos três primeiros níveis é barato, porque em todos eles os repositórios são diretórios Git normais no disco. Comece pelo nível mais baixo que resolve o problema. Se estiver a decidir que outros serviços justificam espaço no mesmo servidor, a lista curta do que vale a pena alojar por conta própria coloca um servidor Git ao lado dos outros serviços que competem por essa RAM.
FAQ
Um VPS de 1 GB consegue executar Forgejo ou Gitea?
Sim, para uma equipa pequena, com SQLite e sem outros serviços pesados no servidor. A documentação do Gitea indica que 1 GB de RAM e 2 núcleos de CPU são normalmente suficientes para equipas e projetos pequenos, e o Forgejo é um fork do Gitea com requisitos semelhantes. Não instale PostgreSQL nem um runner de CI nessa máquina. Se o serviço desaparecer sem erro no próprio log, execute sudo dmesg -T | grep -i oom: uma linha que identifique o processo terminado significa que o killer de falta de memória do kernel o terminou. Nesse caso, a solução é escolher um plano maior, não alterar uma flag de configuração.
Qual é a diferença entre Forgejo e Gitea?
Partilham o histórico da base de código e a maioria das funcionalidades. O Gitea foi criado a partir de um fork do Gogs em 2016, e o Forgejo foi criado a partir de um fork do Gitea no final de 2022, depois de o controlo da marca comercial Gitea ter passado para uma empresa. O Forgejo é publicado pela Codeberg e.V., uma organização sem fins lucrativos na Alemanha, ao abrigo da GPLv3. O Gitea mantém a licença MIT e tem apoio comercial. A diferença prática está no caminho de migração. O Forgejo v10.0, lançado em janeiro de 2025, foi a última versão capaz de importar diretamente uma base de dados do Gitea, e apenas a partir do Gitea v1.22 ou anterior. Por isso, uma instância atual do Gitea não tem uma mudança suportada no local.
Posso executar workflows do GitHub Actions num servidor Git autoalojado?
O Forgejo Actions e o Gitea Actions executam workflows escritos na sintaxe YAML do GitHub Actions, lidos de .forgejo/workflows e .gitea/workflows. Instale um programa runner separado e registe-o na sua instância. Muitas actions publicadas funcionam sem alterações, mas as que chamam a API do GitHub não funcionam. O runner executa código arbitrário dos seus repositórios e inicia um contentor por job. Por isso, atribua-lhe um host próprio ou, pelo menos, um utilizador sem privilégios próprio. Não o execute num servidor de 1 GB que já esteja a executar o forge.
Como faço backup de um servidor Git autoalojado?
Para repositórios bare, git clone --mirror, executado noutra máquina, copia todas as refs e todos os objetos, e git remote update, executado dentro desse mirror, atualiza-o. No Forgejo ou Gitea, os repositórios são apenas parte do estado, porque os issues, os pull requests, os utilizadores e as chaves ficam na base de dados. Use o dump integrado, sudo -u git forgejo dump -c /etc/forgejo/app.ini, ou execute o mesmo comando dentro do contentor numa instalação Docker. Copie o arquivo para fora do servidor e restaure-o uma vez numa máquina de reserva para confirmar que o procedimento funciona.