Tutorial de referência para rodar uma instância Gitea em servidor self-hosted atrás de reverse proxy, com Postgres externo, SSH em porta não-padrão, SSO via Keycloak, CI nativo (Actions + act_runner) e container registry embutido.
Versões alvo: Gitea 1.22.x / 1.23.x (imagens gitea/gitea:1.22-rootless ou mais recente). Os comandos e variáveis seguem o Config Cheat Sheet oficial.
1. Visão geral
Gitea é uma forja Git self-hosted escrita em Go. Roda como um único binário (ou container) com SQLite, MySQL ou PostgreSQL como banco. Comparado às alternativas:
| Aspecto | GitHub.com | GitLab CE | Gitea |
|---|---|---|---|
| Custo de operação | Zero (SaaS) | Pesado: ~4 GB RAM mínimo, Ruby/Rails | Leve: ~200-500 MB RAM, binário Go |
| CI nativo | Actions | GitLab CI | Actions (compatível com sintaxe do GitHub) |
| Container registry | ghcr.io | embutido | embutido (OCI) |
| LFS / Packages | sim | sim | sim |
| SSO OIDC | empresarial | sim | sim |
| Mirror push/pull | limitado | sim | sim |
Quando vale self-host:
- Repositórios privados sem cota
- Dados ficam no seu hardware/jurisdição
- Integração direta com a stack interna (Keycloak, registry, monitoramento)
- Custo previsível (um servidor modesto dá conta de dezenas de devs)
Quando não vale: se você precisa de comunidade pública, discoverability ou pipelines de CI massivos rodando em runners gerenciados, o GitHub continua ganhando por preço/conveniência.
2. Arquitetura
Fluxo geral da infra-alvo deste tutorial:
flowchart LR
user[Dev / Browser]
subgraph Server[Servidor Debian 13]
direction TB
nginx[nginx-proxy<br/>TLS termination]
subgraph net[rede proxy 172.28.0.0/16]
gitea[gitea container<br/>:3000 HTTP<br/>:2222 SSH]
pg[(postgres17:5432)]
runner[act_runner]
end
vol[/data/gitea<br/>repos + LFS + attachments + packages/]
end
user -- HTTPS 443 --> nginx
nginx -- HTTP 3000 --> gitea
user -- SSH :2222 --> gitea
gitea --> pg
gitea --- vol
runner -- long poll HTTP --> giteaPontos importantes:
- SSH em 2222 é publicada direto pelo host (NAT pra container). Não passa por nginx — nginx é HTTP/HTTPS apenas.
- Reverse proxy termina TLS e injeta
X-Forwarded-*. O Gitea precisa saber disso viaROOT_URLpara gerar URLs corretas em e-mails/webhooks. - Storage: por padrão tudo (repos, LFS, attachments, avatars, packages) fica em
/data/gitea. Pode ser separado por seção doapp.ini. - Registry embutido roda no próprio Gitea no endpoint
/v2/. Não é um serviço separado. - Actions runner é um processo externo que faz long-polling no Gitea — precisa de network compartilhada com o container Gitea para resolver o hostname interno.
Fluxo de um push SSH:
sequenceDiagram
participant Dev
participant nginx
participant Host as Servidor (iptables/NAT)
participant Gitea
participant PG as Postgres
Dev->>Host: ssh -p 2222 git@gitea.example.com
Host->>Gitea: forward TCP 2222 -> container :2222
Gitea->>PG: SELECT public_key WHERE fingerprint=...
PG-->>Gitea: ok / user_id
Gitea-->>Dev: handshake ok
Dev->>Gitea: git-receive-pack repo.git
Gitea->>Gitea: pre-receive hook (LFS/branch protection)
Gitea->>PG: INSERT activity, commits
Gitea-->>Dev: success3. Pré-requisitos
- Host: Debian 13 (ou compatível) com Docker Engine 24+ e Docker Compose v2
- Postgres 17 já rodando e acessível (por exemplo, container
postgres17na mesma redeproxy) - DNS apontando para o servidor:
gitea.example.com A <IP-do-servidor> - Portas abertas no firewall:
- 443 (HTTPS via nginx)
- 2222 (SSH Git)
- nginx-proxy com certificado TLS válido (Let’s Encrypt, etc.)
- SMTP opcional para notificações (Postmark, Resend, Mailgun, ou outro)
Verifique a network existente:
docker network ls | grep proxy
docker network inspect proxy --format '{{.IPAM.Config}}'Crie o banco e o usuário no Postgres (uma vez):
docker exec -it postgres17 psql -U postgres -c "CREATE USER gitea WITH PASSWORD '<sua-senha-forte>';"
docker exec -it postgres17 psql -U postgres -c "CREATE DATABASE gitea OWNER gitea ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0;"Nota: use uma senha forte e aleatória. Caso precise trocá-la depois, veja o bloco de rotação de senha na seção de upgrade.
4. Instalação com Docker Compose
Existem duas variantes oficiais da imagem:
gitea/gitea:1.22— root, expõe SSH no próprio container via OpenSSH.gitea/gitea:1.22-rootless— recomendada. Roda como UID 1000, usa o servidor SSH interno (Go), menor superfície de ataque. Volume em/var/lib/giteae config em/etc/gitea.
Bloco do docker-compose.yaml (extrato em /opt/docker/docker-compose.yaml):
services:
gitea:
image: gitea/gitea:1.22-rootless
container_name: gitea
restart: unless-stopped
user: "1000:1000"
networks:
- proxy
depends_on:
- postgres17
volumes:
- /data/gitea/data:/var/lib/gitea
- /data/gitea/config:/etc/gitea
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
# SSH publicado direto no host (NAT). HTTP fica interno na proxy.
- "2222:2222"
environment:
# ---- Identificação do servidor ----
GITEA__server__DOMAIN: gitea.example.com
GITEA__server__ROOT_URL: https://gitea.example.com/
GITEA__server__SSH_DOMAIN: gitea.example.com
GITEA__server__HTTP_PORT: "3000"
# SSH_PORT = porta pública anunciada nas URLs git@host:port; SSH_LISTEN_PORT = porta dentro do container.
GITEA__server__SSH_PORT: "2222"
GITEA__server__SSH_LISTEN_PORT: "2222"
GITEA__server__START_SSH_SERVER: "true"
GITEA__server__LFS_START_SERVER: "true"
GITEA__server__OFFLINE_MODE: "true"
# ---- Banco ----
GITEA__database__DB_TYPE: postgres
GITEA__database__HOST: postgres17:5432
GITEA__database__NAME: gitea
GITEA__database__USER: gitea
GITEA__database__PASSWD: ${GITEA_DB_PASSWORD}
GITEA__database__SSL_MODE: disable
# ---- Cadastro de usuários ----
# Mantenha aberto durante o setup inicial pra criar o admin via web;
# depois feche e use Keycloak.
GITEA__service__DISABLE_REGISTRATION: "true"
GITEA__service__ALLOW_ONLY_EXTERNAL_REGISTRATION: "true"
GITEA__service__SHOW_REGISTRATION_BUTTON: "false"
# ---- Webhooks ----
# Por padrão Gitea bloqueia hosts privados; libere se webhooks vão
# falar com serviços internos da sua infraestrutura.
GITEA__webhook__ALLOWED_HOST_LIST: external,*.example.com,172.28.0.0/16
# ---- Indexer ----
GITEA__indexer__ISSUE_INDEXER_TYPE: bleve
GITEA__indexer__REPO_INDEXER_ENABLED: "true"
GITEA__indexer__REPO_INDEXER_TYPE: bleve
# ---- Actions ----
GITEA__actions__ENABLED: "true"
# ---- USER UID/GID (rootless usa 1000) ----
USER_UID: "1000"
USER_GID: "1000"
networks:
proxy:
external: trueCrie o .env ao lado do compose com:
GITEA_DB_PASSWORD=<sua-senha-forte>Permissões do volume (rootless precisa que o UID 1000 do host seja dono):
sudo mkdir -p /data/gitea/{data,config}
sudo chown -R 1000:1000 /data/giteaSuba:
docker compose -f /opt/docker/docker-compose.yaml up -d gitea
docker logs -f giteaA primeira boot vai criar o schema no Postgres e gerar chaves SSH do servidor em /var/lib/gitea/git/.ssh/.
5. Setup inicial via web
Acesse https://gitea.example.com/install.
Mesmo com env vars setadas, a tela /install aparece uma vez para você confirmar e criar o primeiro admin. Pontos críticos:
- Database settings: já vem preenchido a partir do env. Confirme.
- General settings: confirme
Server DomaineGitea Base URL. - Optional settings → Administrator Account:
- Crie o admin agora, antes de fechar
/install. Se você só fechar sem criar, o primeiro usuário a se registrar via web vira admin (comDISABLE_REGISTRATION=trueisso fica difícil, mas evite ambiguidade).
- Crie o admin agora, antes de fechar
- Clique em Install Gitea. O Gitea grava
INSTALL_LOCK=truenoapp.inie nunca mais reabre/install.
Alternativa CLI (se preferir não usar a web):
docker exec -u 1000 gitea gitea admin user create \
--username admin \
--password '<sua-senha-forte>' \
--email admin@example.com \
--admin --must-change-password=true6. Configuração avançada do app.ini
Depois do install, o arquivo gerado fica em /data/gitea/config/gitea/app.ini (dentro do container: /etc/gitea/app.ini).
Toda config via env var GITEA__section__KEY substitui o app.ini no boot. Você pode editar diretamente o app.ini OU usar env vars — o padrão recomendado é env vars no compose para tudo que muda por ambiente, e deixar o app.ini só com defaults e segredos gerados (SECRET_KEY, INTERNAL_TOKEN).
Seções principais
[server]
DOMAIN = gitea.example.com
ROOT_URL = https://gitea.example.com/
SSH_DOMAIN = gitea.example.com
HTTP_PORT = 3000
SSH_PORT = 2222
SSH_LISTEN_PORT = 2222
START_SSH_SERVER = true
LFS_START_SERVER = true
OFFLINE_MODE = true
[database]
DB_TYPE = postgres
HOST = postgres17:5432
NAME = gitea
USER = gitea
PASSWD = `<sua-senha-forte>`
SSL_MODE = disable
[security]
INSTALL_LOCK = true
SECRET_KEY = `gerado-no-install-NÃO-MEXER`
INTERNAL_TOKEN = `gerado-no-install-NÃO-MEXER`
PASSWORD_HASH_ALGO = pbkdf2_v2
MIN_PASSWORD_LENGTH = 12
DISABLE_GIT_HOOKS = true
[mailer]
ENABLED = true
PROTOCOL = smtps
SMTP_ADDR = smtp.postmarkapp.com
SMTP_PORT = 465
USER = `token-postmark`
PASSWD = `token-postmark`
FROM = "Gitea <gitea@example.com>"
[oauth2]
JWT_SECRET = `gerado-NÃO-MEXER`
[lfs]
PATH = /var/lib/gitea/lfs
HTTP_AUTH_EXPIRY = 24h
MAX_FILE_SIZE = 1073741824 ; 1 GiB
[indexer]
ISSUE_INDEXER_TYPE = bleve
REPO_INDEXER_ENABLED = true
REPO_INDEXER_TYPE = bleve
; Para repos muito grandes (>50 GB de código indexado) considere elasticsearch:
; ISSUE_INDEXER_TYPE = elasticsearch
; ISSUE_INDEXER_CONN_STR = http://elasticsearch:9200
[service]
DISABLE_REGISTRATION = true
ALLOW_ONLY_EXTERNAL_REGISTRATION = true
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL = true
[webhook]
ALLOWED_HOST_LIST = external,*.example.com,172.28.0.0/16
DELIVER_TIMEOUT = 10
[actions]
ENABLED = true
DEFAULT_ACTIONS_URL = githubNotas sobre segurança
SECRET_KEYeINTERNAL_TOKEN: gerados aleatoriamente no install. Nunca rode dois Giteas com o mesmoSECRET_KEY— eles vão competir pela cifragem de tokens 2FA/oauth.PASSWORD_HASH_ALGO = pbkdf2_v2: o padrãopbkdf2é OK;argon2é mais forte mas usa mais memória.DISABLE_GIT_HOOKS = true: impede que usuários (não-admin) instalem git hooks no servidor — manter.INSTALL_LOCK = true: trava/install. Se precisar reabrir (e.g. troca de DB), setafalsee reinicia, mas faça backup antes.
Aplicar mudanças: docker compose restart gitea. Mudanças em [server] ou [database] exigem restart; mudanças em [ui] ou [mailer] na maioria dos casos também.
7. Integração com Keycloak (OAuth2 SSO)
7.1 Criar o client no Keycloak
No realm que você usa (exemplo: corporate):
- Clients → Create client
- Client type: OpenID Connect
- Client ID:
gitea - Name:
Gitea
- Capability config:
- Client authentication: On (vai virar
confidential) - Standard flow: On
- Direct access grants: Off
- Client authentication: On (vai virar
- Login settings:
- Root URL:
https://gitea.example.com - Valid redirect URIs:
https://gitea.example.com/user/oauth2/keycloak/callback - Web origins:
https://gitea.example.com
- Root URL:
- Salvar. Aba Credentials → copiar o Client Secret.
- Client scopes → gitea-dedicated (ou crie um scope
email/profilese ainda não tiver mapper de email): garanta que o token incluiemail,email_verified,preferred_username,name.
URL de discovery (anote):
https://auth.example.com/realms/corporate/.well-known/openid-configuration7.2 Adicionar Auth Source no Gitea
Via UI: Site Administration → Identity & Access → Authentication Sources → Add Authentication Source.
- Authentication Type: OAuth2
- Authentication Name:
keycloak(este nome vira parte do callback URL — por isso o redirect URI acima usa/keycloak/callback) - OAuth2 Provider: OpenID Connect
- Client ID (Key):
gitea - Client Secret:
<secret copiado> - OpenID Connect Auto Discovery URL:
https://auth.example.com/realms/corporate/.well-known/openid-configuration - Additional Scopes:
openid profile email - Required Claim Name / Value: deixe em branco a menos que queira filtrar por role.
- Claim providing group names for this source:
groups(se você quiser mapear roles do Keycloak para teams do Gitea) - Map claims to admin / restricted: opcional, via
Admin Group Claim Value.
Via CLI:
docker exec -u 1000 gitea gitea admin auth add-oauth \
--name keycloak \
--provider openidConnect \
--key gitea \
--secret 'CLIENT_SECRET' \
--auto-discover-url 'https://auth.example.com/realms/corporate/.well-known/openid-configuration' \
--scopes 'openid profile email'7.3 Sync de email/nome/auto-registro
No app.ini (ou via env):
[oauth2_client]
ENABLE_AUTO_REGISTRATION = true
USERNAME = preferred_username ; ou "email"
UPDATE_AVATAR = true
ACCOUNT_LINKING = auto ; auto liga se email bateCom isso, o primeiro login via Keycloak cria a conta Gitea automaticamente, linkando por email se já existir conta local.
8. SSH
8.1 Chaves do servidor
No modo rootless, o sshd interno do Gitea (Go) gera as host keys em:
/var/lib/gitea/git/.ssh/gitea.{rsa,ecdsa,ed25519}Faça backup desses arquivos no primeiro boot — se forem perdidos, todos os clients vão receber warning de “host key changed”.
Listar:
docker exec -u 1000 gitea ls -la /var/lib/gitea/git/.ssh/8.2 Porta 2222 no cliente
Como o SSH não está na 22, configure o cliente em ~/.ssh/config:
Host gitea.example.com
HostName gitea.example.com
Port 2222
User git
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yesA partir daí, git clone git@gitea.example.com:<usuario>/<projeto>.git funciona sem -p 2222.
Cadastre sua chave pública em https://gitea.example.com/user/settings/keys.
Teste:
ssh -T git@gitea.example.com
# Hi there, <usuario>! You've successfully authenticated...9. Reverse proxy nginx
Server block para gitea.example.com (em apps/infra/nginx/conf.d/gitea.conf):
upstream gitea_backend {
server gitea:3000;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name gitea.example.com;
ssl_certificate /etc/nginx/certs/gitea.example.com.crt;
ssl_certificate_key /etc/nginx/certs/gitea.example.com.key;
# Upload de repos / LFS / imagens de container podem ser grandes
client_max_body_size 2048M;
# Push grande / git-lfs / clone inicial precisa de timeout generoso
proxy_read_timeout 600s;
proxy_send_timeout 600s;
proxy_connect_timeout 30s;
location / {
proxy_pass http://gitea_backend;
proxy_http_version 1.1;
# Websocket upgrade (Actions logs streaming + live updates)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Redirect HTTP -> HTTPS
server {
listen 80;
server_name gitea.example.com;
return 301 https://$host$request_uri;
}Se a instância deve ficar restrita ao range da VPN/rede confiável, adicione no server block uma allowlist:
allow 10.8.0.0/24; # rede da VPN
allow 192.168.1.0/24; # LAN
deny all;Recarregar nginx:
docker exec nginx-proxy nginx -t && docker exec nginx-proxy nginx -s reload10. Gitea Actions
10.1 Habilitar no servidor
No app.ini (ou env var):
[actions]
ENABLED = true
DEFAULT_ACTIONS_URL = githubDEFAULT_ACTIONS_URL=github significa que uses: actions/checkout@v4 será resolvido contra github.com/actions/checkout. Você também pode apontar para um mirror interno (e.g. self).
Reinicia o container.
10.2 Obter o token de registro
Três escopos possíveis:
| Escopo | URL |
|---|---|
| Instância | https://gitea.example.com/-/admin/actions/runners |
| Organização | https://gitea.example.com/<org>/-/settings/actions/runners |
| Repositório | https://gitea.example.com/<owner>/<repo>/settings/actions/runners |
Clique em Create new Runner e copie o token. Alternativamente via CLI:
docker exec -u 1000 gitea gitea actions generate-runner-token10.3 Subir act_runner via docker compose
Adicione no mesmo docker-compose.yaml:
gitea-runner:
image: gitea/act_runner:latest
container_name: gitea-runner
restart: unless-stopped
networks:
- proxy
depends_on:
- gitea
environment:
CONFIG_FILE: /config.yaml
GITEA_INSTANCE_URL: https://gitea.example.com
GITEA_RUNNER_REGISTRATION_TOKEN: ${RUNNER_TOKEN}
GITEA_RUNNER_NAME: runner-01
GITEA_RUNNER_LABELS: "ubuntu-latest:docker://node:20-bullseye,docker:docker://docker:dind"
volumes:
- /data/gitea/runner/config.yaml:/config.yaml
- /data/gitea/runner/data:/data
- /var/run/docker.sock:/var/run/docker.sockGere um config.yaml base:
docker run --rm gitea/act_runner:latest generate-config > /data/gitea/runner/config.yamlSubir:
RUNNER_TOKEN=xxxxx docker compose up -d gitea-runner
docker logs -f gitea-runnerVocê deve ver runner registered successfully e depois polling logs. Confirme em /-/admin/actions/runners que ele aparece como Idle.
10.4 Workflow de exemplo
.gitea/workflows/ci.yml (sintaxe igual GitHub Actions):
name: CI
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install
run: npm ci
- name: Test
run: npm test
- name: Build container
run: |
docker build -t gitea.example.com/${{ gitea.repository }}:${{ gitea.sha }} .
- name: Login to registry
run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login gitea.example.com -u ${{ gitea.actor }} --password-stdin
- name: Push
run: docker push gitea.example.com/${{ gitea.repository }}:${{ gitea.sha }}Secrets ficam em Settings → Actions → Secrets (escopo repo ou org). REGISTRY_TOKEN deve ser um Personal Access Token com escopo write:package.
11. Container Registry
O Gitea expõe um registry OCI em https://gitea.example.com/v2/.
11.1 Criar PAT
Settings → Applications → Generate New Token. Selecione apenas os escopos mínimos:
read:packagewrite:package(se for fazer push)
11.2 Login / push / pull
echo "$PAT" | docker login gitea.example.com -u <usuario> --password-stdin
docker tag minha-app:dev gitea.example.com/<usuario>/minha-app:1.0.0
docker push gitea.example.com/<usuario>/minha-app:1.0.0
docker pull gitea.example.com/<usuario>/minha-app:1.0.0O namespace pode ser usuário ou organização (gitea.example.com/<owner>/<image>).
Listar pacotes na UI: https://gitea.example.com/<owner>/-/packages.
11.3 Registry embutido vs registry dedicado (Nexus / Harbor)
| Critério | Gitea registry | Nexus / registry dedicado |
|---|---|---|
| Setup | zero (vem junto) | container separado |
| RBAC | herda do Gitea (org/repo) | políticas próprias |
| Proxy/cache de Docker Hub | não | sim (Nexus pull-through) |
| Limpeza/retention policies | manual / via API | sim, sofisticado |
| Multi-tenant pesado | OK até dezenas de imagens | melhor pra centenas |
Regra prática: imagens da própria org (build artifact de um repo) ficam no Gitea registry — mantém auditoria junto do código. Imagens operacionais/infra (bases, mirrors de upstream) ficam num registry dedicado (registry.example.com, geralmente Nexus ou Harbor) onde tem retention policies e pull-through cache.
12. Mirroring e webhooks
12.1 Push mirror para o GitHub
Em Settings → Mirror Settings do repositório:
- Mirror From URL: vazio (este é push mirror)
- Push Mirror → Add Push Mirror
- Git Remote URL:
https://github.com/<usuario>/<projeto>.git - Authorization → Username: seu user GitHub
- Password: PAT do GitHub com
reposcope - Interval:
8h0m0s
- Git Remote URL:
A cada intervalo, Gitea faz git push --mirror. Útil pra ter um espelho público sem usar o GitHub como primário.
12.2 Webhooks para CI externo
Settings → Webhooks → Add Webhook → Gitea (genérico HTTP JSON):
- Target URL:
https://ci.exemplo.com/hook - HTTP Method: POST
- Content Type:
application/json - Secret: chave aleatória (vai vir no header
X-Gitea-Signature) - Trigger On: Push, Pull Request, Release
Importante: se o destino estiver em rede privada, ele precisa estar em GITEA__webhook__ALLOWED_HOST_LIST (veja seção 4).
13. Backup
13.1 gitea dump (oficial)
A doc oficial pede para parar o Gitea para garantir consistência entre DB e repos. Em ambientes pequenos com pouca atividade, dump quente costuma funcionar — mas para backup confiável, pare.
# Pare o gitea (opcional mas recomendado)
docker compose stop gitea
# Crie o dump (rode dentro do container, como UID 1000)
docker compose run --rm -u 1000 -v /data/backups:/backup gitea \
gitea dump -c /etc/gitea/app.ini --file /backup/gitea-$(date +%Y%m%d).zip
docker compose start giteaO .zip resultante contém:
app.ini+ diretóriocustom/- Dump SQL do banco (XORM, em
gitea-db.sql) - Repos completos (
repos/) - Anexos, avatars, LFS
- Logs (opcional)
13.2 Alternativa: pg_dump + tar do volume
Mais rápido e suporta backup quente bem:
# Banco
docker exec postgres17 pg_dump -U gitea -F c gitea > /data/backups/gitea-db-$(date +%F).dump
# Volume (LFS e repos)
tar --use-compress-program=zstd -cf /data/backups/gitea-data-$(date +%F).tar.zst -C /data/gitea dataPara restaurar:
# Banco
docker exec -i postgres17 pg_restore -U gitea -d gitea --clean --if-exists < gitea-db-2026-05-11.dump
# Dados
docker compose stop gitea
tar -xf gitea-data-2026-05-11.tar.zst -C /data/gitea
docker compose start gitea
# Regerar hooks (obrigatório se você moveu de path/instalação)
docker exec -u 1000 gitea gitea admin regenerate hooks13.3 Automação
Cron job no host (/etc/cron.d/gitea-backup):
0 3 * * * root /usr/local/bin/gitea-backup.sh >> /var/log/gitea-backup.log 2>&1E rotação simples (find /data/backups -mtime +14 -delete).
14. Upgrade
Gitea segue semver. Migrações de schema rodam automaticamente no boot.
Procedimento
Leia o changelog em https://blog.gitea.com/. Procure por “breaking changes” ou “config rename”.
Backup completo (seção 13). Sem isso, downgrade entre minors é impossível.
Bump da tag no
docker-compose.yaml:image: gitea/gitea:1.23-rootless # antes: 1.22-rootlessPull e recreate:
docker compose pull gitea docker compose up -d gitea docker logs -f giteaAcompanhe os logs até “Listen: http://0.0.0.0:3000”. Em DBs grandes, migrações podem levar alguns minutos.
Rollback
- Patch (1.22.0 → 1.22.1): seguro voltar a tag, schema é o mesmo.
- Minor (1.22 → 1.23): schema muda. Só funciona restaurando o backup do banco anterior. Sem backup, você está preso na nova versão.
Rotação de senha do banco
Procedimento para trocar a senha do usuário do banco:
# 1. Trocar no Postgres
docker exec -it postgres17 psql -U postgres -c "ALTER USER gitea WITH PASSWORD '<nova-senha-forte>';"
# 2. Atualizar .env
sed -i 's/^GITEA_DB_PASSWORD=.*/GITEA_DB_PASSWORD=<nova-senha-forte>/' /opt/docker/.env
# 3. Recreate (só envvars novas, não precisa pull)
docker compose up -d giteaO Gitea reabre o pool com a senha nova no próximo boot.
15. Troubleshooting
15.1 fatal: the remote end hung up unexpectedly em push grande
Aumente os timeouts no nginx (já feito na seção 9) e no Gitea:
[server]
PROXY_PROTOCOL_HEADER_TIMEOUT = 5s
[http]
; nada — Go default é generosoPush de >100 MB? Use Git LFS em vez de blob puro.
15.2 Permission denied (publickey) em SSH
Cheque na ordem:
Sua chave pública está em
https://gitea.example.com/user/settings/keys?O cliente está usando a chave certa?
ssh -vT git@gitea.example.comProcure por
Offering public key: ~/.ssh/id_ed25519.A porta 2222 está aberta?
nc -zv gitea.example.com 2222As host keys do servidor não foram regeradas? Se sim, remova a entrada antiga em
~/.ssh/known_hosts.
15.3 Permissão negada no volume (rootless)
Erros como chown /var/lib/gitea: permission denied no boot:
sudo chown -R 1000:1000 /data/giteaO UID 1000 é hardcoded na imagem rootless — não tente rodar com outro UID via user: se não souber o que está fazendo.
15.4 Runner Actions stuck em “Idle” mas não pega job
Confirme que o container
gitea-runnerestá na mesma network (proxy) dogitea.Confirme
GITEA_INSTANCE_URLaponta para o hostname externo HTTPS (não usehttp://gitea:3000em produção — o runner usa essa URL pra callback de logs e o cert TLS importa).Verifique labels no workflow vs labels do runner. Se o workflow pede
runs-on: ubuntu-lateste seu runner só temself-hosted, ninguém pega.Logs:
docker logs --tail 200 gitea-runner
15.5 Webhook falhando com “Resolved IP is not allowed”
Hostname destino não está em ALLOWED_HOST_LIST. Adicione a env var (GITEA__webhook__ALLOWED_HOST_LIST) e reinicie.
15.6 gitea dump falha com “permission denied”
Está rodando como root mas o volume é owned por 1000. Use docker compose run -u 1000 (veja seção 13).
15.7 /install não abre depois de mudar o DB
INSTALL_LOCK=true no app.ini. Edite manualmente para false, reinicie, refaça o install.