{"id":502,"date":"2026-09-06T00:11:12","date_gmt":"2026-09-06T03:11:12","guid":{"rendered":"https:\/\/www.linuxpro.com.br\/2026\/09\/gitea-git-self-hosted-runner-tea\/"},"modified":"2026-10-05T14:37:03","modified_gmt":"2026-10-05T17:37:03","slug":"gitea-git-self-hosted-runner-tea","status":"publish","type":"post","link":"https:\/\/www.linuxpro.com.br\/es\/2026\/09\/gitea-git-self-hosted-runner-tea\/","title":{"rendered":"Gitea: Git self-hosted con Actions, runner y tea CLI"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" alt=\"Mascote do LinuxPro encaixando um push de c\u00f3digo no servidor com o logo do Gitea, ao lado do banco MariaDB e do runner de CI com containers na esteira\" src=\"\/wp-content\/uploads\/2026\/09\/gitea-v4.webp\" width=\"1486\" height=\"856\" \/><\/p>\n<div style=\"background:#fff8e1;border-left:4px solid #8b1a1a;padding:.8em 1em;margin:1em 0\"><strong>Atualiza\u00e7\u00e3o (05\/10\/2026):<\/strong> guia revisado para o <strong>Gitea 28.0.0<\/strong>, de 29\/09\/2026 \u2014 a vers\u00e3o que abandonou o prefixo \u201c1.\u201d \u2014, e para o runner 4.1.0. Veja a se\u00e7\u00e3o <a href=\"#gitea-28\">O que muda na 28<\/a> antes de atualizar uma inst\u00e2ncia 1.27: h\u00e1 duas mudan\u00e7as incompat\u00edveis.<\/div>\n<p><strong>Gitea<\/strong> (<em>Git with a cup of tea<\/em>) \u00e9 a forge self-hosted em Go: reposit\u00f3rio Git, PR, issues, wiki, registry de pacotes e CI\/CD no mesmo bin\u00e1rio. Fork do <a href=\"\/2017\/04\/instalando-gogs-no-ubuntu\/\">Gogs<\/a> em 2016, licen\u00e7a MIT, cabe num VPS de 1 GB. A vers\u00e3o est\u00e1vel \u00e9 a <strong>28.0.0<\/strong>, de 29\/09\/2026. Este texto sobe a inst\u00e2ncia com Docker Compose e <strong>MariaDB<\/strong>, registra o <strong>Gitea Runner 4<\/strong> para rodar Actions e instala o CLI oficial <strong>tea 0.16<\/strong>.<\/p>\n<p><!-- more --><\/p>\n<p>O Git em si \u2014 BitKeeper, os dez dias de Linus, o 2.55 \u2014 est\u00e1 em <a href=\"\/2026\/09\/a-historia-do-git\/\">A hist\u00f3ria do Git<\/a>. O cheatsheet do dia a dia, em <a href=\"\/2017\/04\/git-simples-e-rapido\/\">Git simples e r\u00e1pido<\/a>. Aqui o assunto \u00e9 a forge: o GitHub que voc\u00ea roda na sua m\u00e1quina.<\/p>\n<h2>O que o Gitea \u00e9 (e o que n\u00e3o \u00e9)<\/h2>\n<p>N\u00e3o \u00e9 <a href=\"\/2026\/09\/a-historia-do-gitlab\/\">GitLab<\/a> CE. N\u00e3o puxa Elasticsearch, n\u00e3o pede 8 GB de RAM para o hello world. \u00c9 um bin\u00e1rio (ou um container) + um banco. O Gitea fala com SQLite3, MySQL\/MariaDB, PostgreSQL e MSSQL. <strong>SQLite<\/strong> vem embutido e resolve lab, Raspberry Pi e um usu\u00e1rio s\u00f3. Em produ\u00e7\u00e3o com time e Actions, use <strong>MariaDB<\/strong> (ou MySQL: o driver \u00e9 o mesmo, <code>DB_TYPE=mysql<\/code>).<\/p>\n<p>Actions existem desde a 1.19 e v\u00eam ligadas por padr\u00e3o desde a 1.21: o YAML \u00e9 o da GitHub Actions, mas quem executa \u00e9 um runner pr\u00f3prio, que voc\u00ea registra. O <code>tea<\/code> \u00e9 o <code>gh<\/code> dessa casa. O Forgejo \u00e9 o fork comunit\u00e1rio de 2022; a base \u00e9 comum, mas os dois projetos j\u00e1 divergem. C\u00f3digo: <a href=\"https:\/\/github.com\/go-gitea\/gitea\">github.com\/go-gitea\/gitea<\/a>. Docs: <a href=\"https:\/\/docs.gitea.com\/\">docs.gitea.com<\/a>.<\/p>\n<h2>Arquitetura<\/h2>\n<p>Tr\u00eas pe\u00e7as. O <strong>Gitea<\/strong> atende web, API e SSH e guarda os metadados no <strong>MariaDB<\/strong>. O <strong>runner<\/strong> pode ficar na mesma m\u00e1quina ou em outra: ele s\u00f3 abre conex\u00f5es de sa\u00edda para o Gitea, busca os jobs na fila e sobe um container por job no Docker. O ponto que mais derruba instala\u00e7\u00e3o nova \u00e9 o \u00faltimo passo: o container do job faz o checkout pela <code>ROOT_URL<\/code> da inst\u00e2ncia, ent\u00e3o ela precisa ser um endere\u00e7o que o job alcan\u00e7a \u2014 nunca <code>localhost<\/code>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" alt=\"Diagrama da arquitetura: desenvolvedor acessa o Gitea por HTTP 3000 e SSH 222, o Gitea usa MariaDB na porta 3306, o gitea-runner busca jobs no Gitea e sobe containers de job pelo Docker, que fazem checkout pela ROOT_URL\" src=\"\/wp-content\/uploads\/2026\/09\/gitea-arquitetura-runner.webp\" width=\"1200\" height=\"660\" \/><\/p>\n<h2 id=\"gitea-28\">O que muda na 28<\/h2>\n<p>A vers\u00e3o que viria a ser 1.28.0 saiu como <strong>28.0.0<\/strong>: o projeto <a href=\"https:\/\/blog.gitea.com\/release-of-28.0.0\/\">deixou de usar o prefixo \u201c1.\u201d<\/a>. N\u00e3o houve 27 vers\u00f5es perdidas \u2014 o n\u00famero que antes ficava depois do \u201c1.\u201d passou para a frente. Ajuste scripts que comparam vers\u00f5es ou que procuram tags <code>1.*<\/code>.<\/p>\n<p>Os destaques: <strong>log de auditoria<\/strong>, <strong>contas de bot<\/strong> gerenciadas pela interface, API e CLI, <strong>deploy tokens<\/strong> por HTTPS, <strong>impersona\u00e7\u00e3o<\/strong> de usu\u00e1rio pelo administrador, prote\u00e7\u00e3o de branch que bloqueia o merge enquanto houver revis\u00e3o pendente de <em>code owner<\/em>, filtros no diff e uma <strong>fila de builds<\/strong> para as Actions. As Actions tamb\u00e9m ganharam matriz din\u00e2mica, <code>max-parallel<\/code> e mais endpoints de API para gerenciar execu\u00e7\u00f5es e logs. A release traz corre\u00e7\u00f5es de seguran\u00e7a, como a rejei\u00e7\u00e3o de objetos Git inv\u00e1lidos ou duplicados no push e a garantia de que execu\u00e7\u00f5es de PR vindas de fork continuem atr\u00e1s da aprova\u00e7\u00e3o; os detalhes seriam publicados uma semana depois do lan\u00e7amento.<\/p>\n<p>Duas mudan\u00e7as s\u00e3o incompat\u00edveis e pedem aten\u00e7\u00e3o <em>antes<\/em> do upgrade:<\/p>\n<ul>\n<li><strong>Hist\u00f3rico de Actions passa a expirar.<\/strong> O novo <code>RUN_RETENTION_DAYS<\/code> apaga execu\u00e7\u00f5es conclu\u00eddas com mais de 400 dias, junto com jobs e tarefas, na limpeza da meia-noite. Para manter tudo, defina <code>RUN_RETENTION_DAYS = 0<\/code> na se\u00e7\u00e3o <code>[actions]<\/code> (no Compose, <code>GITEA__actions__RUN_RETENTION_DAYS: \"0\"<\/code>). Agora <code>0<\/code> significa \u201cguardar para sempre\u201d tamb\u00e9m em <code>LOG_RETENTION_DAYS<\/code> e <code>ARTIFACT_RETENTION_DAYS<\/code>, que antes apagavam tudo na limpeza seguinte com esse valor.<\/li>\n<li><strong>Tr\u00e1fego Git de sa\u00edda passa por um proxy interno.<\/strong> Migra\u00e7\u00f5es, espelhos e outras opera\u00e7\u00f5es de rede do Git agora obedecem \u00e0s regras de egress. O preset <code>external<\/code> deixou de existir: para negar tudo por padr\u00e3o, use <code>EGRESS_MODE = strict<\/code> e liste o que \u00e9 permitido (em <code>[migrations]<\/code> para migra\u00e7\u00f5es e espelhos; em <code>[security]<\/code> para webhooks e OAuth2). Curingas em IP e o item <code>*<\/code> n\u00e3o s\u00e3o mais aceitos, dom\u00ednios seguem a sintaxe do curl (<code>example.com<\/code> inclui os subdom\u00ednios; <code>*.example.com<\/code>, s\u00f3 os subdom\u00ednios) e uma entrada inv\u00e1lida em <code>BLOCKED_HOST_LIST<\/code> impede o Gitea de subir. <code>ALLOWED_DOMAINS<\/code>, <code>BLOCKED_DOMAINS<\/code> e <code>ALLOW_LOCALNETWORKS<\/code> ficam obsoletos em favor de <code>ALLOWED_HOST_LIST<\/code> e <code>BLOCKED_HOST_LIST<\/code>.<\/li>\n<\/ul>\n<p>Detalhe para quem baixa bin\u00e1rios por script: a 28 n\u00e3o publica mais builds x86 de 32 bits nem as variantes <code>gogit<\/code>, e os nomes de arquivo perderam o sufixo de vers\u00e3o do sistema operacional.<\/p>\n<h2>Subir com Docker Compose e MariaDB<\/h2>\n<p>Imagem oficial: <code>docker.gitea.com\/gitea:28.0.0<\/code> (testada aqui: sobe, cria o administrador pela CLI e responde <code>{\"version\":\"28.0.0\"}<\/code> em <code>\/api\/v1\/version<\/code>). Porta 3000 na web; 222 no host mapeada para o SSH do container (22), para n\u00e3o brigar com o sshd da m\u00e1quina. Troque <code>SEU_IP<\/code> pelo IP ou dom\u00ednio da m\u00e1quina e as senhas por valores seus.<\/p>\n<pre><code class=\"language-bash\">mkdir -p ~\/gitea &amp;&amp; cd ~\/gitea<\/code><\/pre>\n<pre><code class=\"language-yaml\"># docker-compose.yml\nnetworks:\n  gitea:\n\nvolumes:\n  gitea-data:\n  mariadb-data:\n\nservices:\n  server:\n    image: docker.gitea.com\/gitea:28.0.0\n    container_name: gitea\n    restart: always\n    environment:\n      USER_UID: \"1000\"\n      USER_GID: \"1000\"\n      GITEA__database__DB_TYPE: mysql\n      GITEA__database__HOST: db:3306\n      GITEA__database__NAME: gitea\n      GITEA__database__USER: gitea\n      GITEA__database__PASSWD: troque-esta-senha\n      GITEA__server__ROOT_URL: http:\/\/SEU_IP:3000\/\n      GITEA__server__SSH_DOMAIN: SEU_IP\n      GITEA__server__SSH_PORT: \"222\"\n    networks:\n      - gitea\n    volumes:\n      - gitea-data:\/data\n      - \/etc\/timezone:\/etc\/timezone:ro\n      - \/etc\/localtime:\/etc\/localtime:ro\n    ports:\n      - \"3000:3000\"\n      - \"222:22\"\n    depends_on:\n      - db\n    healthcheck:\n      test: [\"CMD\", \"curl\", \"-f\", \"http:\/\/localhost:3000\/\"]\n      interval: 10s\n      timeout: 5s\n      retries: 6\n      start_period: 30s\n\n  db:\n    image: docker.io\/library\/mariadb:11\n    restart: always\n    environment:\n      MARIADB_ROOT_PASSWORD: troque-esta-senha-root\n      MARIADB_DATABASE: gitea\n      MARIADB_USER: gitea\n      MARIADB_PASSWORD: troque-esta-senha\n    networks:\n      - gitea\n    volumes:\n      - mariadb-data:\/var\/lib\/mysql\n    command: [\"--character-set-server=utf8mb4\", \"--collation-server=utf8mb4_bin\"]<\/code><\/pre>\n<pre><code class=\"language-bash\">docker compose up -d\ndocker compose ps\ndocker compose logs -f server<\/code><\/pre>\n<p>Abra <code>http:\/\/SEU_IP:3000<\/code> e complete o assistente. Os campos do banco j\u00e1 v\u00eam preenchidos pelas vari\u00e1veis: host <code>db<\/code> (nome do servi\u00e7o, n\u00e3o localhost), tipo <strong>MySQL<\/strong> (\u00e9 como a UI chama o MariaDB). O primeiro usu\u00e1rio criado vira administrador. Depois do wizard o <code>app.ini<\/code> fica em <code>\/data\/gitea\/conf\/app.ini<\/code> no volume; qualquer chave pode ser sobrescrita por vari\u00e1vel no formato <code>GITEA__secao__CHAVE<\/code>.<\/p>\n<p><code>SSH_PORT: \"222\"<\/code> s\u00f3 muda a URL de clone que a interface mostra; o SSH continua escutando na 22 dentro do container. Clone:<\/p>\n<pre><code class=\"language-bash\">git clone ssh:\/\/git@SEU_IP:222\/usuario\/repo.git<\/code><\/pre>\n<p>Chave p\u00fablica: <a href=\"\/2017\/04\/ssh-sem-senha\/\">SSH sem senha<\/a>. Em produ\u00e7\u00e3o coloque HTTPS na frente (nginx ou <a href=\"\/2026\/10\/caddy-no-linux-servidor-web-com-https-automatico\/\">Caddy<\/a>), troque a <code>ROOT_URL<\/code> para <code>https:\/\/git.seudominio\/<\/code> e, se ningu\u00e9m de fora deve criar conta, defina <code>GITEA__service__DISABLE_REGISTRATION: \"true\"<\/code>.<\/p>\n<h2>SQLite: um container, zero banco extra<\/h2>\n<p>Para testar na m\u00e1quina ou num Pi, tire o servi\u00e7o <code>db<\/code> e as vari\u00e1veis <code>GITEA__database__*<\/code>. No assistente escolha <strong>SQLite3<\/strong>; o arquivo fica em <code>\/data\/gitea\/gitea.db<\/code>.<\/p>\n<pre><code class=\"language-yaml\">services:\n  server:\n    image: docker.gitea.com\/gitea:28.0.0\n    container_name: gitea\n    restart: always\n    environment:\n      USER_UID: \"1000\"\n      USER_GID: \"1000\"\n    volumes:\n      - .\/gitea-data:\/data\n    ports:\n      - \"3000:3000\"\n      - \"222:22\"<\/code><\/pre>\n<p>Serve para um usu\u00e1rio e poucos repos. Com time, CI e registry, fique no MariaDB: o SQLite serializa as escritas e engasga quando v\u00e1rios jobs e usu\u00e1rios gravam ao mesmo tempo.<\/p>\n<h2>Bin\u00e1rio com systemd, se n\u00e3o quiser container<\/h2>\n<p>Caminho da documenta\u00e7\u00e3o oficial, com o Git do sistema e um usu\u00e1rio <code>git<\/code> dedicado:<\/p>\n<pre><code class=\"language-bash\">sudo apt install -y git\nsudo wget -O \/usr\/local\/bin\/gitea \\\n  https:\/\/dl.gitea.com\/gitea\/28.0.0\/gitea-28.0.0-linux-amd64\nsudo chmod +x \/usr\/local\/bin\/gitea\n\nsudo adduser --system --shell \/bin\/bash --gecos 'Git Version Control' \\\n  --group --disabled-password --home \/home\/git git\nsudo mkdir -p \/var\/lib\/gitea\/{custom,data,log} \/etc\/gitea\nsudo chown -R git:git \/var\/lib\/gitea\nsudo chmod -R 750 \/var\/lib\/gitea\nsudo chown root:git \/etc\/gitea\nsudo chmod 770 \/etc\/gitea   # o assistente web grava o app.ini aqui<\/code><\/pre>\n<pre><code class=\"language-ini\"># \/etc\/systemd\/system\/gitea.service\n[Unit]\nDescription=Gitea\nAfter=network.target mariadb.service\n\n[Service]\nUser=git\nGroup=git\nWorkingDirectory=\/var\/lib\/gitea\/\nExecStart=\/usr\/local\/bin\/gitea web --config \/etc\/gitea\/app.ini\nEnvironment=USER=git HOME=\/home\/git GITEA_WORK_DIR=\/var\/lib\/gitea\nRestart=always\n\n[Install]\nWantedBy=multi-user.target<\/code><\/pre>\n<pre><code class=\"language-bash\">sudo systemctl daemon-reload\nsudo systemctl enable --now gitea\n# depois do assistente, trave a configura\u00e7\u00e3o:\nsudo chmod 750 \/etc\/gitea\nsudo chmod 640 \/etc\/gitea\/app.ini<\/code><\/pre>\n<p>Mais sobre units e <code>systemctl<\/code>: <a href=\"\/2026\/09\/dominando-o-systemd-comandos-essenciais\/\">Dominando o systemd<\/a>. O bin\u00e1rio j\u00e1 traz SQLite; MariaDB\/MySQL e PostgreSQL entram no <code>[database]<\/code> do <code>app.ini<\/code>. Cada bin\u00e1rio em <a href=\"https:\/\/dl.gitea.com\/gitea\/\">dl.gitea.com\/gitea<\/a> vem com <code>.sha256<\/code>, assinatura GPG (<code>.asc<\/code>) e pacote Sigstore (<code>.sigstore.json<\/code>) \u2014 confira antes de colocar em produ\u00e7\u00e3o.<\/p>\n<h2>Gitea Actions e o Runner<\/h2>\n<p>O Gitea n\u00e3o executa workflow sozinho. Quem pega o job \u00e9 o <strong>runner<\/strong>: o bin\u00e1rio chama-se <code>gitea-runner<\/code> (o projeto antes se chamava <code>act_runner<\/code>), o c\u00f3digo est\u00e1 em <a href=\"https:\/\/gitea.com\/gitea\/runner\">gitea.com\/gitea\/runner<\/a> e a imagem oficial \u00e9 <code>docker.io\/gitea\/runner<\/code>. A vers\u00e3o atual \u00e9 a <strong>4.1.0<\/strong>, de 01\/10\/2026, que acrescentou um backend para executar os jobs no Kubernetes. O YAML vive em <code>.gitea\/workflows\/<\/code> e usa o mesmo esquema da GitHub Actions.<\/p>\n<p>Token de registro: <em>Site Administration \u2192 Actions \u2192 Runners \u2192 Create new runner<\/em> (vale tamb\u00e9m por organiza\u00e7\u00e3o ou por reposit\u00f3rio, nas configura\u00e7\u00f5es de cada um). Se puder, rode o runner em outra m\u00e1quina \u2014 o job consome CPU e RAM de quem o executa, e a forge n\u00e3o deve disputar recurso com um build.<\/p>\n<pre><code class=\"language-yaml\"># acrescente em services: no mesmo docker-compose.yml\n  runner:\n    image: docker.io\/gitea\/runner:4\n    restart: always\n    environment:\n      GITEA_INSTANCE_URL: http:\/\/SEU_IP:3000\/\n      GITEA_RUNNER_REGISTRATION_TOKEN: COLE_O_TOKEN\n      GITEA_RUNNER_NAME: linuxpro-1\n    networks:\n      - gitea\n    volumes:\n      - .\/runner-data:\/data\n      - \/var\/run\/docker.sock:\/var\/run\/docker.sock\n    depends_on:\n      server:\n        condition: service_healthy<\/code><\/pre>\n<p><code>GITEA_INSTANCE_URL<\/code> deve ser a mesma <code>ROOT_URL<\/code> do Gitea. D\u00e1 para registrar com <code>http:\/\/server:3000<\/code> pela rede do compose, mas o checkout dentro do job usa a <code>ROOT_URL<\/code>, e o container do job n\u00e3o resolve o nome <code>server<\/code>.<\/p>\n<p>A imagem vem em tr\u00eas sabores:<\/p>\n<ul>\n<li><code>gitea\/runner:4<\/code> \u2014 s\u00f3 o runner; usa o Docker do host pelo socket montado. Simples, mas quem monta o <code>docker.sock<\/code> tem o equivalente a root na m\u00e1quina, e os jobs enxergam os outros containers dela.<\/li>\n<li><code>gitea\/runner:4-dind<\/code> \u2014 traz um dockerd pr\u00f3prio, isolado do host; exige <code>privileged: true<\/code> e n\u00e3o usa o socket.<\/li>\n<li><code>gitea\/runner:4-dind-rootless<\/code> \u2014 mesmo esquema, com o daemon rodando como UID 1000; menos poder, mais limita\u00e7\u00f5es de rede e cgroups.<\/li>\n<\/ul>\n<p>O volume <code>\/data<\/code> guarda o arquivo de registro <code>.runner<\/code>. Sem ele, cada recria\u00e7\u00e3o do container registra um runner novo e o antigo fica \u00f3rf\u00e3o na UI. Esse volume n\u00e3o guarda o cache de imagens: no <code>dind<\/code>, monte tamb\u00e9m <code>\/var\/lib\/docker<\/code> se n\u00e3o quiser baixar tudo de novo a cada restart.<\/p>\n<p>Quem vem da s\u00e9rie 3 deve ler as notas da 4.0: com <code>cache.external_server<\/code> os jobs passam a falar com o cache atrav\u00e9s do runner, jobs em <code>container.network: bridge<\/code> perdem acesso ao cache e aos artefatos, e <code>*<\/code> em <code>valid_volumes<\/code> n\u00e3o atravessa mais <code>\/<\/code> (use <code>**<\/code>). A 4.0 tamb\u00e9m trouxe a action <code>builtin:checkout<\/code>, que n\u00e3o precisa de download nem de Node na imagem do job.<\/p>\n<p>Workflow m\u00ednimo no reposit\u00f3rio:<\/p>\n<pre><code class=\"language-yaml\"># .gitea\/workflows\/ci.yml\nname: ci\non:\n  push:\n    branches: [main]\n  pull_request:\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n      - run: echo \"commit ${{ gitea.sha }}\" &amp;&amp; uname -a<\/code><\/pre>\n<p>O label <code>ubuntu-latest<\/code> vem por padr\u00e3o e aponta para a imagem <code>docker.gitea.com\/runner-images:ubuntu-latest<\/code>. O contexto aceita <code>gitea.*<\/code> e tamb\u00e9m <code>github.*<\/code>, ent\u00e3o a maior parte das actions p\u00fablicas funciona sem mudan\u00e7a; <code>uses: actions\/checkout@v4<\/code> \u00e9 baixado do GitHub, porque <code>DEFAULT_ACTIONS_URL<\/code> vale <code>github<\/code> por padr\u00e3o \u2014 em rede isolada, espelhe as actions na pr\u00f3pria inst\u00e2ncia. Pipeline em conceito: <a href=\"\/2019\/12\/devops-ci-cd\/\">DevOps: o que \u00e9 CI\/CD?<\/a>.<\/p>\n<h2>tea CLI<\/h2>\n<p><code>tea<\/code> \u00e9 o cliente oficial (MIT, Go): issues, PR, releases, labels, login em v\u00e1rias inst\u00e2ncias e <code>tea pr checkout<\/code> no reposit\u00f3rio local. A est\u00e1vel \u00e9 a <strong>0.16.0<\/strong>, de 10\/09\/2026, que trouxe <code>tea login status<\/code> e leitura da descri\u00e7\u00e3o de issue ou PR a partir de arquivo ou stdin.<\/p>\n<pre><code class=\"language-bash\">sudo curl -fsSL -o \/usr\/local\/bin\/tea \\\n  https:\/\/dl.gitea.com\/tea\/0.16.0\/tea-0.16.0-linux-amd64\nsudo chmod +x \/usr\/local\/bin\/tea\ntea --version<\/code><\/pre>\n<p>Token: <em>Settings \u2192 Applications \u2192 Generate New Token<\/em>, com permiss\u00e3o de leitura e escrita em repository e issue. Login:<\/p>\n<pre><code class=\"language-bash\">tea login add --name casa --url http:\/\/SEU_IP:3000 --token SEU_TOKEN\ntea login list\ntea repos\ntea issues\ntea pulls<\/code><\/pre>\n<p>Dentro de um clone hospedado nessa inst\u00e2ncia o <code>tea<\/code> descobre o remote sozinho. Exemplos do dia:<\/p>\n<pre><code class=\"language-bash\">tea issues list --state open\ntea pulls checkout 12\ntea pulls approve 12 \"LGTM\"\ntea pulls merge 12\ntea releases create --tag v1.0.0 --title v1.0.0 --note \"primeiro corte\"<\/code><\/pre>\n<p>Para <code>git push<\/code> por HTTPS sem senha, <code>tea login add<\/code> aceita <code>--git-credentials<\/code> e se registra como credential helper do Git. Em esta\u00e7\u00e3o com navegador existe o fluxo <code>--oauth<\/code>; em servidor headless e em job de CI, token continua o caminho simples. Bin\u00e1rios: <a href=\"https:\/\/dl.gitea.com\/tea\/\">dl.gitea.com\/tea<\/a>. C\u00f3digo: <a href=\"https:\/\/gitea.com\/gitea\/tea\">gitea.com\/gitea\/tea<\/a>.<\/p>\n<h2>Backup e upgrade<\/h2>\n<p>O <code>gitea dump<\/code> empacota reposit\u00f3rios, configura\u00e7\u00e3o, anexos e um dump SQL do banco num zip. No container ele precisa rodar como <code>git<\/code> e dentro de um diret\u00f3rio grav\u00e1vel:<\/p>\n<pre><code class=\"language-bash\">docker compose exec -u git -w \/tmp server \\\n  gitea dump -c \/data\/gitea\/conf\/app.ini --file \/tmp\/gitea-dump.zip\ndocker compose cp server:\/tmp\/gitea-dump.zip .\/gitea-dump-$(date +%F).zip\ndocker compose exec server rm \/tmp\/gitea-dump.zip<\/code><\/pre>\n<p>N\u00e3o existe comando de restore: a volta \u00e9 manual (descompactar, devolver arquivos e importar o SQL). Por isso vale ter tamb\u00e9m o dump nativo do banco \u2014 <code>mariadb-dump<\/code> \u2014 agendado e testado; o <a href=\"\/2026\/09\/databasus-backup-de-postgresql-mysql-e-mariadb-com-restore-testado\/\">Databasus<\/a> faz isso com restaura\u00e7\u00e3o verificada.<\/p>\n<p>Upgrade da 1.27 para a 28, na ordem:<\/p>\n<ol>\n<li>Fa\u00e7a o <code>gitea dump<\/code> e o dump nativo do banco.<\/li>\n<li>Decida a reten\u00e7\u00e3o das Actions: se quiser manter todo o hist\u00f3rico, acrescente <code>GITEA__actions__RUN_RETENTION_DAYS: \"0\"<\/code> ao servi\u00e7o <code>server<\/code> <em>antes<\/em> de subir a nova vers\u00e3o.<\/li>\n<li>Se voc\u00ea usa <code>ALLOWED_DOMAINS<\/code>, <code>BLOCKED_DOMAINS<\/code>, <code>ALLOW_LOCALNETWORKS<\/code> ou o preset <code>external<\/code>, reescreva as regras com <code>ALLOWED_HOST_LIST<\/code>, <code>BLOCKED_HOST_LIST<\/code> e <code>EGRESS_MODE<\/code>.<\/li>\n<li>Troque a tag no compose (<code>1.27.3<\/code> \u2192 <code>28.0.0<\/code>) e rode <code>docker compose pull &amp;&amp; docker compose up -d<\/code>. As migra\u00e7\u00f5es do banco rodam na subida.<\/li>\n<li>Confira <code>docker compose logs server<\/code>: o Gitea avisa no in\u00edcio quando encontra regra de egress inv\u00e1lida ou lista de hosts sem <code>EGRESS_MODE<\/code> expl\u00edcito.<\/li>\n<\/ol>\n<p>N\u00e3o troque a imagem rootful pela rootless no mesmo volume.<\/p>\n<h2>O que ficou<\/h2>\n<p>Gitea 28 + MariaDB 11 + runner 4.1 + tea 0.16 \u00e9 uma forge completa num VPS pequeno. SQLite para o primeiro <code>docker compose up<\/code>; MariaDB quando o time e o CI entram. Acerte a <code>ROOT_URL<\/code> antes de registrar o runner e metade dos problemas de Actions some. O Git continua o de sempre; a camada social \u2014 PR, Actions, registry \u2014 \u00e9 o que o GitHub cobrou e o Gogs n\u00e3o chegou a amarrar.<\/p>\n<p>Mais: <a href=\"\/2026\/09\/a-historia-do-git\/\">hist\u00f3ria do Git<\/a>, <a href=\"\/2026\/09\/a-historia-do-gitlab\/\">hist\u00f3ria do GitLab<\/a>, <a href=\"\/2026\/09\/a-historia-do-docker\/\">hist\u00f3ria do Docker<\/a>, <a href=\"\/2017\/04\/git-simples-e-rapido\/\">cheatsheet do Git<\/a> e o <a href=\"\/2017\/04\/instalando-gogs-no-ubuntu\/\">Gogs em 2017<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Gitea es la forge auto-hospedada en Go: Git, PR, issues, registry y CI\/CD en el mismo binario. Gu\u00eda actualizada para Gitea 28.0.0 \u2014 la versi\u00f3n que abandon\u00f3 el prefijo \u201c1.\u201d \u2014, con Docker Compose y MariaDB, runner 4.1, tea CLI y la hoja de ruta de actualizaci\u00f3n de la 1.27.<\/p>","protected":false},"author":0,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[45,46,2,111,120],"tags":[105,156,48,201,204,203,202],"class_list":["post-502","post","type-post","status-publish","format-standard","hentry","category-desenv","category-devops","category-linux","category-opensource","category-servidores","tag-ci-cd","tag-docker","tag-git","tag-gitea","tag-runner","tag-self-hosted","tag-tea"],"_links":{"self":[{"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/posts\/502","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/comments?post=502"}],"version-history":[{"count":7,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/posts\/502\/revisions"}],"predecessor-version":[{"id":1800,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/posts\/502\/revisions\/1800"}],"wp:attachment":[{"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/media?parent=502"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/categories?post=502"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.linuxpro.com.br\/es\/wp-json\/wp\/v2\/tags?post=502"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}