Gitea

Lightweight self-hosted Git service — deployment, CI/CD, authentication & operations

01

Overview

Gitea is a lightweight, self-hosted Git service written in Go. It is a community-driven fork of Gogs, created in November 2016 to accelerate development under a more open governance model. Gitea ships as a single binary with no required external dependencies beyond a database (Redis, Memcached, etc. are optional for caching/sessions), making it one of the simplest Git platforms to deploy and operate.

Gitea provides a GitHub-like experience for teams that want to self-host their source code, issues, pull requests, and CI/CD pipelines. It supports organizations, teams, branch protection, code review, a multi-format package registry (including OCI containers), and built-in CI via Gitea Actions.

Why Gitea over GitLab?

  • Resource efficiency — Gitea runs comfortably on 512 MB–1 GB RAM and a single CPU core. GitLab requires 4+ GB RAM minimum and multiple background processes
  • Single binary — No Ruby, Node.js, or complex process supervision. Download, configure, run
  • Fast startup — Gitea starts in seconds. GitLab can take minutes to fully initialize
  • Simple upgrades — Replace the binary, run migrations, restart. No multi-step upgrade procedures
  • GitHub Actions compatibility — Gitea Actions uses GitHub Actions workflow syntax, reducing the learning curve
Positioning

Gitea occupies the space between bare-bones Git hosting (Gogs, cgit) and full DevOps platforms (GitLab, GitHub Enterprise). It is ideal for small-to-medium teams that need pull requests, code review, and CI but do not need GitLab's built-in security scanning or advanced project management features. Note that Gitea does include a built-in package registry with OCI container support. For teams that outgrow Gitea, migration to GitLab or GitHub is straightforward.

02

Architecture

Gitea's architecture is deliberately simple. A single Go binary handles HTTP/HTTPS, SSH, Git protocol operations, the web UI, API, and background tasks. There is no required message queue, no required Redis dependency, and no separate worker process needed — though Redis/Memcached can optionally be used for caching and session storage at scale.

User (Browser) ——> [HTTPS] ——> Reverse Proxy (Nginx/Caddy) User (Git CLI) ——> [SSH] ——> Gitea SSH Server (built-in) | v +-----------------+ | Gitea | | (Go binary) | +--------+--------+ | +-------------+-------------+ | | v v +---------------+ +----------------+ | Database | | Git Repos | | (SQLite/PG/ | | (bare repos | | MySQL/MSSQL) | | on disk) | +---------------+ +----------------+

Core components

Go Binary

Single statically-compiled binary. Includes the web server, Git smart HTTP handler, built-in SSH server, task scheduler, and Gitea Actions orchestrator. Typical memory usage is 100–300 MB for small-to-medium instances.

Database

Stores users, organizations, issues, pull requests, comments, webhook configs, and CI job metadata. Supports SQLite (small teams), PostgreSQL (recommended for production), MySQL/MariaDB, and MSSQL.

Git Repositories

Bare Git repositories stored on the local filesystem. Default path: /data/gitea/repositories (Docker) or /var/lib/gitea/repositories (system install). Can be on local disk, NFS, or block storage.

Built-in SSH Server

Gitea includes its own SSH server (written in Go) for Git-over-SSH operations. Alternatively, it can use the system's OpenSSH server with an authorized_keys integration or SSH certificate authentication.

Recommendation

For production deployments with more than a handful of users, use PostgreSQL as the database backend. SQLite works well for small teams (under ~10 users) but lacks concurrent write performance. For the Git repository storage, use fast local SSD or block storage — NFS can introduce latency and locking issues with Git operations.

03

Deployment

Gitea can be deployed as a standalone binary, a Docker container, via Kubernetes Helm charts, or through system package managers. The Docker approach is the most common for production.

Docker deployment (recommended)

services:
  gitea:
    image: gitea/gitea:1.25
    environment:
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=secret
      - GITEA__server__ROOT_URL=https://git.example.com/
      - GITEA__server__SSH_DOMAIN=git.example.com
    volumes:
      - gitea-data:/data
    ports:
      - "3000:3000"
      - "2222:22"
    depends_on:
      - db
    restart: always

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=gitea
    volumes:
      - db-data:/var/lib/postgresql/data
    restart: always

volumes:
  gitea-data:
  db-data:

Binary installation

  • Download the appropriate binary from dl.gitea.com for your OS/architecture
  • Make it executable: chmod +x gitea
  • Run the initial setup wizard at http://localhost:3000/install
  • Use systemd to manage the process in production

Kubernetes / Helm

  • Official Helm chart: helm repo add gitea-charts https://dl.gitea.com/charts/
  • Deploy with: helm install gitea gitea-charts/gitea
  • Supports external PostgreSQL, persistent volumes for repositories, ingress configuration
  • Consider ReadWriteMany PVCs if running multiple replicas (or use a shared filesystem)

Reverse proxy configuration

# Nginx reverse proxy for Gitea
server {
    listen 443 ssl;
    http2 on;
    server_name git.example.com;

    ssl_certificate     /etc/ssl/certs/git.example.com.crt;
    ssl_certificate_key /etc/ssl/private/git.example.com.key;

    location / {
        proxy_pass http://localhost:3000;
        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;

        # Large file uploads (Git LFS, repo pushes)
        client_max_body_size 512M;
    }
}
Rootless Containers

Gitea provides a gitea/gitea:1.25-rootless image that runs as a non-root user (UID 1000). This is the recommended image for security-conscious deployments. The rootless image uses port 3000 for HTTP and port 2222 for SSH by default (cannot bind privileged ports below 1024).

04

Repository Management

Gitea provides a full-featured Git hosting experience with organizations, teams, and fine-grained access controls.

Organizations and teams

  • Organizations — Group repositories under a shared namespace (e.g., git.example.com/myorg/repo)
  • Teams — Define groups within an organization with specific permissions (Owner, Admin, Write, Read)
  • Visibility — Repositories can be public, private, or internal (visible to all signed-in users)

Branch protection

Protection Rules

  • Require pull request reviews before merging
  • Require status checks to pass (CI/CD)
  • Restrict who can push to protected branches
  • Block force pushes and branch deletion
  • Require signed commits

Pull Request Workflow

  • Merge, rebase, squash merge strategies
  • Inline code review with line comments
  • Approval/rejection review system
  • Auto-merge when all checks pass
  • Draft pull requests

Releases and packages

  • Releases — Create tagged releases with release notes and file attachments (binaries, archives)
  • Package registry — Built-in support for 20+ formats including Container (OCI), npm, PyPI, Maven, NuGet, Cargo, Composer, Conan, Conda, Go, Helm, RubyGems, Alpine, Debian, RPM, Swift, Pub, Chef, CRAN, Vagrant, and a generic HTTP registry
  • Git LFS — Large File Storage support for tracking binary assets. LFS objects are stored separately from the Git repository
Recommendation

Enable branch protection on main/master from day one. Require at least one review approval and passing CI checks before merge. This is cheap to set up and prevents accidental direct pushes to production branches.

05

Gitea Actions

Gitea Actions is Gitea's built-in CI/CD system, introduced in Gitea 1.19. It is compatible with GitHub Actions workflow syntax, meaning most GitHub Actions workflows can run on Gitea with minimal or no changes.

How it works

  • Workflows are defined in .gitea/workflows/ (or .github/workflows/ for GitHub compatibility)
  • Gitea dispatches jobs to act runners — self-hosted agents that execute workflow steps
  • Runners use act (the same tool behind nektos/act) to parse and execute GitHub Actions-compatible workflows
  • Supports Docker-based and host-based execution environments

Runner setup

# Install act_runner (download from https://dl.gitea.com/act_runner/ or releases page)
wget https://dl.gitea.com/act_runner/latest/act_runner-linux-amd64
chmod +x act_runner-linux-amd64

# Register the runner with your Gitea instance
./act_runner-linux-amd64 register \
  --instance https://git.example.com \
  --token YOUR_RUNNER_TOKEN \
  --name my-runner \
  --labels ubuntu-latest:docker://node:20-bookworm

# Start the runner as a daemon
./act_runner-linux-amd64 daemon

Workflow example

# .gitea/workflows/ci.yaml
name: CI
on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: '1.22'
      - run: go test ./...

  build:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4
      - run: go build -o myapp ./cmd/server
      - uses: actions/upload-artifact@v4
        with:
          name: myapp
          path: myapp
Compatibility Notes

While Gitea Actions is largely compatible with GitHub Actions, some differences exist: not all GitHub-hosted runner features are available, some third-party Actions may reference GitHub-specific APIs, and the GITHUB_TOKEN equivalent is GITEA_TOKEN (an auto-generated short-lived token per job). Notable unsupported workflow features include concurrency, permissions, timeout-minutes, and continue-on-error. The actions/ namespace actions (checkout, setup-go, etc.) work because Gitea mirrors them, but marketplace-only actions may need manual import.

06

Authentication

Gitea supports multiple authentication sources that can be used simultaneously. External auth sources are configured in the admin panel under Site Administration → Authentication Sources.

Supported providers

Enterprise LDAP / Active Directory

Bind or simple authentication against LDAP/AD servers. Supports LDAP search with custom filters, TLS/STARTTLS, attribute mapping for email, name, and SSH keys. Users are created on first login.

Enterprise OAuth2 / OpenID Connect

Generic OAuth2 provider support plus built-in presets for GitHub, GitLab, Bitbucket, Google, Microsoft, Keycloak, and more. OIDC auto-discovery via .well-known/openid-configuration.

Enterprise Only SAML

SAML 2.0 Identity Provider integration for enterprise SSO (ADFS, Okta, Entra ID). Available only in Gitea Enterprise, not the open-source community edition. For community-edition SSO, use OAuth2/OIDC instead. Configure via metadata URL or manual certificate upload.

Simple Other Methods

  • PAM — Pluggable Authentication Modules (Linux system users)
  • SMTP — Authenticate against an SMTP server
  • Reverse proxy — Trust a header set by a reverse proxy (e.g., X-WEBAUTH-USER)

External auth configuration

Authentication sources are managed via the admin panel or the app.ini configuration file. Key settings:

[service]
DISABLE_REGISTRATION = true        ; Disable self-registration
ALLOW_ONLY_EXTERNAL_REGISTRATION = true  ; Only allow external auth
ENABLE_REVERSE_PROXY_AUTHENTICATION = false

[oauth2]
ENABLE = true
JWT_SIGNING_ALGORITHM = RS256
Security

In production, disable self-registration (DISABLE_REGISTRATION = true) and require authentication via LDAP or OAuth2/OIDC. If using reverse proxy auth, ensure the proxy header cannot be spoofed by end users — strip the auth header from incoming external requests at the edge proxy.

07

Migration

Gitea includes a built-in migration tool that can import repositories and metadata from other Git platforms. Migration is available from the web UI (New Migration) or the API.

Supported migration sources

SourceReposIssuesPRsWikisLabelsMilestonesReleases
GitHubYesYesYesYesYesYesYes
GitLabYesYesYesYesYesYesYes
BitbucketYesYesYesNoNoYesNo
GogsYesYesNoYesYesYesNo
OneDevYesYesNoNoYesYesNo
Plain Git URLYesNoNoNoNoNoNo

Migration considerations

  • Authentication tokens — A personal access token from the source platform is required for private repos and metadata (issues, PRs)
  • User mapping — Migrated issues/PRs reference original usernames. Users are not automatically created on the Gitea instance
  • Attachments — Issue and PR attachments may or may not be migrated depending on the source platform's API
  • Large repositories — Git LFS objects are migrated if LFS is enabled on the Gitea instance. Very large repos may hit timeout limits
  • Rate limits — GitHub and GitLab APIs have rate limits. For bulk migrations, use tokens with higher rate limits or stagger migration jobs
Tip

For large-scale migrations (hundreds of repos), use the Gitea API to script the migration process rather than the web UI. The POST /api/v1/repos/migrate endpoint accepts all migration parameters and can be called in a loop with appropriate delays for rate limiting.

08

Administration

Gitea provides an admin panel, CLI tools, and a comprehensive REST API for managing the instance.

Admin panel

  • Dashboard — System status, memory usage, goroutines, Git version, database info
  • User management — Create, edit, disable, delete users. Promote/demote admins
  • Repository management — View all repos, force-delete, transfer ownership
  • Authentication sources — Add/edit LDAP, OAuth2, SAML providers
  • System notices — View errors and warnings from background tasks

CLI administration

# Create an admin user
gitea admin user create --username admin --password secret --email admin@example.com --admin

# Change a user's password
gitea admin user change-password --username admin --password newpassword

# Regenerate hooks for all repositories
gitea admin repo-sync-releases

# Dump the database and repositories for backup
gitea dump -c /etc/gitea/app.ini

API

Gitea provides a Swagger/OpenAPI compliant REST API at /api/swagger. The API covers all major operations: user management, repository CRUD, issues, pull requests, organizations, teams, webhooks, and administration.

# List all repositories via API
curl -H "Authorization: token YOUR_API_TOKEN" \
  https://git.example.com/api/v1/repos/search?limit=50

# Create a webhook
curl -X POST -H "Authorization: token YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"type":"slack","config":{"url":"https://hooks.slack.com/...","content_type":"json"},"events":["push"],"active":true}' \
  https://git.example.com/api/v1/repos/myorg/myrepo/hooks

Repository maintenance

  • Git GC — Run git gc periodically to pack objects and reduce disk usage. Can be triggered from the admin panel or via cron
  • Webhooks — Configure per-repo or org-level webhooks for Slack, Discord, Mattermost, MS Teams, Telegram, or generic HTTP endpoints
  • Mirror repos — Set up push or pull mirrors to keep repositories synchronized with external Git servers
Recommendation

Configure Gitea's built-in cron tasks in app.ini to automate repository maintenance. Enable repo_health_check, repo_archive_cleanup, and git_gc_repos on a daily or weekly schedule. This prevents repository bloat and catches corruption early.

09

Gitea vs GitLab vs Forgejo

Understanding how Gitea compares to alternatives helps position it correctly for different use cases.

Feature comparison

FeatureGiteaGitLab CEForgejo
LanguageGoRuby + GoGo (fork of Gitea)
Min RAM~256 MB~4 GB~256 MB
Single binaryYesNo (Omnibus)Yes
Built-in CI/CDGitea ActionsGitLab CI/CDForgejo Actions
Container registryPackage registry (OCI)Full registryPackage registry (OCI)
Security scanningNoSAST, DAST, dependencyNo
Project managementIssues, projects (basic)Issues, epics, boardsIssues, projects (basic)
Pages (static sites)No (use CI deploy or third-party)GitLab PagesNo built-in (third-party via Codeberg Pages)
LicenseMITMIT (CE)GPL-3.0+ (copyleft, since v9.0)
GovernanceCompany-backed (Gitea Ltd)GitLab Inc.Community (Codeberg e.V.)

Forgejo: the Gitea fork

Forgejo began as a soft fork of Gitea in late 2022 by the Codeberg community after governance concerns arose when Gitea's maintainers formed a for-profit company (Gitea Ltd) and transferred the project's domain and trademarks to it. In February 2024, Forgejo declared itself a hard fork, and the codebases have been diverging since. Forgejo aims to remain fully community-governed under the Codeberg e.V. non-profit.

  • Forgejo is API-compatible with Gitea and can be used as a drop-in replacement
  • Forgejo regularly merges upstream Gitea changes but also develops its own features (federation, Forgejo Actions improvements)
  • For organizations that prioritize open governance and copyleft licensing, Forgejo may be preferred
  • For organizations that want commercial support or are comfortable with company-backed open source, Gitea is the direct choice
Decision Guide

Choose Gitea if you want a lightweight, well-established Git platform with commercial support options. Choose Forgejo if community governance and copyleft licensing matter to your organization. Choose GitLab if you need a full DevSecOps platform with built-in security scanning, advanced project management, and enterprise features — and have the infrastructure to support it.

10

Consultant's Checklist

Pre-deployment

  • Choose database backend (PostgreSQL for production, SQLite for small teams/dev)
  • Plan storage for Git repositories (fast local SSD or block storage, avoid NFS if possible)
  • Determine authentication method (LDAP, OAuth2/OIDC, SAML, or local accounts)
  • Configure reverse proxy with TLS (Nginx, Caddy, or Traefik)
  • Set ROOT_URL to the public-facing URL
  • Decide on SSH access method (built-in SSH server vs system OpenSSH)
  • Plan for Git LFS if binary assets are expected

Production hardening

  • Disable self-registration (DISABLE_REGISTRATION = true)
  • Set SECRET_KEY and INTERNAL_TOKEN to strong random values
  • Use the rootless Docker image (gitea/gitea:*-rootless)
  • Enable HTTPS and HSTS at the reverse proxy level
  • Configure [session] COOKIE_SECURE = true and [security] CSRF_COOKIE_HTTP_ONLY = true (CSRF httponly is true by default)
  • Set up Gitea Actions runners on separate infrastructure from the Gitea server
  • Restrict admin API token usage and rotate tokens regularly

CI/CD setup

  • Verify Gitea Actions is enabled (default since v1.21; [actions] ENABLED = true in app.ini)
  • Deploy at least one act_runner and register it with the instance
  • Configure runner labels to match workflow runs-on values
  • Mirror commonly used GitHub Actions to your Gitea instance for reliability
  • Set up branch protection rules requiring CI checks to pass before merge

Operations

  • Automate database backups (daily pg_dump or gitea dump)
  • Back up Git repository storage directory
  • Enable cron tasks for git gc, repo health checks, and archive cleanup
  • Monitor disk usage for repositories and LFS objects
  • Set up log aggregation (Gitea logs + reverse proxy access logs)
  • Document upgrade procedure (stop, replace binary/image, start — migrations run automatically)
  • Configure webhooks for key events (push, PR, release) to team chat