Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Gitea Administrator & Developer Guide

Generated May 2026 from go-gitea/gitea at 19f0169

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

1. Architecture Overview

Gitea is a self-hosted Git forge written in Go that provides repository hosting, issue tracking, pull requests, CI/CD (Gitea Actions), a built-in package registry, and extensive authentication support — all compiled into a single binary. Forked from Gogs, Gitea has evolved into a fully independent project with a mature plugin and extension architecture.

Gitea is designed for minimal operational overhead: a single binary, an optional external database, and a configuration file are all that is required. It embeds web assets, templates, locale files, and default configurations directly into the binary (when built with the bindata tag), enabling zero-dependency deployment.

Architecture Diagram

                        ┌──────────────────────────────────────────┐
                        │              Gitea Binary                 │
                        │                                          │
   HTTP/HTTPS ──────────┤  ┌─────────┐   ┌──────────┐             │
   (go-chi)             │  │ Routers │──▶│ Services │             │
                        │  │ (web,   │   │ (pull,   │             │
   SSH ─────────────────┤  │  api,   │   │  issue,  │             │
   (built-in/OpenSSH)   │  │  private│   │  auth,   │             │
                        │  │  install│   │  repo,   │             │
                        │  └────┬────┘   │  webhook,│             │
                        │       │        │  mailer, │             │
                        │       ▼        │  actions)│             │
                        │  ┌─────────┐   └────┬─────┘             │
                        │  │ Models  │◀───────┘                   │
                        │  │ (XORM)  │                            │
                        │  └────┬────┘                            │
                        │       │        ┌───────────┐            │
                        │       ▼        │ Modules   │            │
                        │  ┌─────────┐   │ (git,     │            │
                        │  │Database │   │  setting, │            │
                        │  │(PG/MySQL│   │  markup,  │            │
                        │  │/SQLite/ │   │  storage, │            │
                        │  │ MSSQL)  │   │  queue,   │            │
                        │  └─────────┘   │  indexer, │            │
                        │                │  cache,   │            │
                        │                │  log)     │            │
                        │                └───────────┘            │
                        │                                          │
                        │  ┌───────────────────────────────────┐   │
                        │  │        Embedded Resources          │   │
                        │  │ templates/ options/ web_src/       │   │
                        │  └───────────────────────────────────┘   │
                        └──────────────────────────────────────────┘
                                          │
                    ┌─────────────────────┼─────────────────────┐
                    ▼                     ▼                     ▼
            ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
            │  Object Store │    │  Git Repos   │    │   Indexer    │
            │  (local/S3/   │    │  (filesystem)│    │(bleve/ES/    │
            │   Azure Blob) │    │              │    │ meilisearch) │
            └──────────────┘    └──────────────┘    └──────────────┘

Component Inventory

Component Path Role
CLI entry point main.go → cmd/ Binary entry, subcommand dispatch (urfave/cli v3)
Web routers routers/web/ HTTP request routing (go-chi/chi v5)
API routers routers/api/ REST API (Swagger-documented) and package registry
Private API routers/private/ Internal API for SSH → web communication
Install router routers/install/ First-run setup wizard
Services services/ Business logic (pull, issue, auth, webhook, mailer, actions, etc.)
Models models/ Data access layer (XORM ORM, database-agnostic)
Modules modules/ Shared utilities (git, setting, markup, storage, queue, indexer, etc.)
Templates templates/ Go HTML templates for the web UI
Frontend web_src/ JavaScript, CSS, Vue components
Options options/ Embedded resources (locales, labels, licenses, gitignore templates, READMEs)
Contrib contrib/ Service manager configs (systemd, supervisor, launchd), IDE configs
Docker docker/ Dockerfile and rootless variant
Snap snap/ Snap package definition

Technology Stack

Layer Technology
Language Go 1.26+
HTTP Router go-chi/chi v5
CLI urfave/cli v3
ORM XORM
Template Engine Go html/template
Frontend Vanilla JS, Fomantic UI, Vue.js (selective), TypeScript
Markup Rendering goldmark (Markdown), go-org (Org mode), chroma (syntax highlighting)
Git Integration Native Git CLI + go-git library
Authentication LDAP, OAuth2 (goth), SAML, SMTP, PAM, SSPI, WebAuthn
Search bleve (built-in), Elasticsearch, Meilisearch
Cache In-memory, Redis, Memcache, TwoQueue
Queue Level, Channel, Redis
Storage Local filesystem, MinIO/S3, Azure Blob Storage
Database PostgreSQL, MySQL/MariaDB, SQLite, MSSQL

Data Flow: Git Push

1. User pushes via SSH or HTTPS
2. SSH: built-in SSH server or OpenSSH → cmd/serv → cmd/hook
   HTTPS: routers/web/githttp.go → repo.ServiceReceivePack
3. Git hooks fire: pre-receive → update → post-receive
4. Post-receive hook calls Gitea internal API (routers/private/)
5. Services layer processes: update refs, trigger webhooks, update indexer
6. Notification system dispatches: email, webhooks, UI notifications
7. If Actions workflows exist: queue CI jobs for runners

2. System Requirements

Component Minimum Production Recommended
CPU 1 core 2-4 cores
RAM 512 MB 2-4 GB
Disk 1 GB (binary + DB) SSD, sized for repositories
Go 1.26+ (build only) Latest stable
Git 2.0+ Latest stable (2.40+)
Database SQLite 3 PostgreSQL 12+
OS Linux, macOS, Windows, FreeBSD Linux (amd64/arm64)
Node.js 18+ (build frontend only) N/A for binary deployment

Note

Gitea's Git integration shells out to the system git binary. The installed Git version directly affects feature availability: LFS requires Git >= 2.1.2, certain advanced features require Git >= 2.40. Gitea validates the Git version at startup and fails fast with a clear error if the version is unsupported.

Supported Databases

Database Minimum Version Notes
SQLite 3 Default, good for small instances (<50 users)
PostgreSQL 12+ Recommended for production
MySQL 8.0+ MariaDB 10.4+ also supported
MSSQL 2012+ Microsoft SQL Server

3. Configuration Reference

Gitea reads configuration from an INI file at {WorkPath}/custom/conf/app.ini. Every setting can be overridden via environment variables with the GITEA__ prefix pattern.

Configuration Files

File Format Location Purpose
app.ini INI {CustomPath}/conf/app.ini Primary configuration
Environment vars GITEA__SECTION__KEY Process environment Override any app.ini setting
{CustomPath}/conf/app.ini INI Default: {WorkPath}/custom/conf/ Per-instance overrides

Global CLI Flags

Flag Alias Default Description
--work-path -w Binary directory Gitea working directory
--config -c {WorkPath}/custom/conf/app.ini Custom config file path
--custom-path -C {WorkPath}/custom Custom file directory

Environment Variable Override System

Gitea supports environment variable configuration with the pattern GITEA__SECTION__KEY=value. The double underscore separates section and key names. Special characters in section or key names are hex-encoded:

# [server] HTTP_PORT = 8080
GITEA__server__HTTP_PORT=8080

# [database] DB_TYPE = postgres
GITEA__database__DB_TYPE=postgres

# Hex encoding for dots: [service.explore] DISABLE_USERS_PAGE
GITEA__service_0X2E_explore__DISABLE_USERS_PAGE=true

The __FILE suffix reads the value from a file (useful for Docker secrets):

GITEA__database__PASSWD__FILE=/run/secrets/db_password

Important

Environment variables take precedence over app.ini settings. They are applied at startup and are not written back to the config file.

[server] Section

Key Type Default Description
PROTOCOL string http Protocol: http, https, unix, fcgi, fcgi+unix
DOMAIN string localhost Domain for SSH URLs and email links
HTTP_ADDR string 0.0.0.0 Listen address
HTTP_PORT int 3000 Listen port
ROOT_URL string (computed) Full external URL
SSH_DOMAIN string (same as DOMAIN) SSH domain name
SSH_PORT int 22 SSH port
START_SSH_SERVER bool false Start built-in SSH server
SSH_LISTEN_HOST string 0.0.0.0 SSH listen address
SSH_LISTEN_PORT int 22 SSH listen port
CERT_FILE string TLS certificate path
KEY_FILE string TLS private key path
LFS_START_SERVER bool false Enable Git LFS server
OFFLINE_MODE bool false Disable external CDN resources
LANDING_PAGE string home Landing page: home, explore, organizations, login

[database] Section

Key Type Default Description
DB_TYPE string sqlite3 Database engine: postgres, mysql, mssql, sqlite3
HOST string 127.0.0.1:3306 Database host and port
NAME string gitea Database name
USER string root Database user
PASSWD string Database password
SSL_MODE string disable PostgreSQL SSL mode
LOG_SQL bool false Log SQL queries

[repository] Section

Key Type Default Description
ROOT string {AppDataPath}/gitea-repositories Repository storage path
DEFAULT_BRANCH string main Default branch name
DEFAULT_PRIVATE string last Default visibility: last, private, public
ENABLE_PUSH_CREATE_USER bool false Allow creating repos via push
ENABLE_PUSH_CREATE_ORG bool false Allow creating org repos via push
DISABLED_REPO_UNITS string Disabled repository units
DEFAULT_REPO_UNITS string Default repo units for new repos

[service] Section

Key Type Default Description
DISABLE_REGISTRATION bool false Disable user self-registration
REQUIRE_SIGNIN_VIEW bool false Require login to view anything
REGISTER_EMAIL_CONFIRM bool false Require email confirmation
ENABLE_NOTIFY_MAIL bool false Enable email notifications
DEFAULT_KEEP_EMAIL_PRIVATE bool false Hide user email by default
ENABLE_CAPTCHA bool false Enable CAPTCHA on registration

[admin] Section

Key Type Default Description
DISABLE_REGULAR_ORG_CREATION bool false Only admins can create organizations
DEFAULT_EMAIL_NOTIFICATIONS string enabled Default email notification setting
USER_DISABLED_FEATURES string Comma-separated disabled features: deletion, manage_ssh_keys, manage_gpg_keys, manage_mfa, manage_credentials, change_username, change_full_name
EXTERNAL_USER_DISABLE_FEATURES string Additional disabled features for external auth users

[actions] Section

Key Type Default Description
ENABLED bool true Enable Gitea Actions
DEFAULT_ACTIONS_URL string github Action resolution source: github or self
LOG_RETENTION_DAYS int 365 Days to retain action logs
LOG_COMPRESSION string zstd Log compression: zstd or none
ARTIFACT_RETENTION_DAYS int 90 Days to retain artifacts
ZOMBIE_TASK_TIMEOUT duration 10m Timeout for zombie task detection
ENDLESS_TASK_TIMEOUT duration 3h Timeout for endless tasks
ABANDONED_JOB_TIMEOUT duration 24h Timeout for abandoned jobs
SKIP_WORKFLOW_STRINGS string [skip ci],[ci skip],[no ci],[skip actions],[actions skip] Commit message patterns that skip workflows
WORKFLOW_DIRS string .gitea/workflows,.github/workflows Directories to search for workflow files

Important

When setting DEFAULT_ACTIONS_URL = self, action references like uses: actions/checkout@v4 resolve against your own Gitea instance instead of GitHub. Ensure the required actions are available in your instance.

[api] Section

Key Type Default Description
ENABLE_SWAGGER bool true Enable Swagger API documentation UI
MAX_RESPONSE_ITEMS int 50 Maximum items in paginated responses
DEFAULT_PAGING_NUM int 30 Default page size
DEFAULT_MAX_BLOB_SIZE int64 10485760 Max blob size in API responses (10 MB)
DEFAULT_MAX_RESPONSE_SIZE int64 104857600 Max total response size (100 MB)

[attachment] Section

Key Type Default Description
ENABLED bool true Enable file attachments
ALLOWED_TYPES string .avif,.csv,.docx,... (see below) Allowed file extensions
MAX_SIZE int64 100 Max attachment size in MB
MAX_FILES int 5 Max files per upload
STORAGE_TYPE string local Storage backend: local, minio

Default allowed types: .avif,.cpuprofile,.csv,.dmp,.docx,.fodg,.fodp,.fods,.fodt,.gif,.gz,.jpeg,.jpg,.json,.jsonc,.log,.md,.mov,.mp4,.odf,.odg,.odp,.ods,.odt,.patch,.pdf,.png,.pptx,.svg,.tgz,.txt,.webm,.webp,.xls,.xlsx,.zip

[cache] Section

Key Type Default Description
ADAPTER string memory Cache backend: memory, redis, memcache, twoqueue
INTERVAL int 60 Garbage collection interval (seconds)
HOST string Connection string for redis/memcache
ITEM_TTL duration 16h Cache item TTL

[cache.last_commit] Section

Key Type Default Description
ITEM_TTL duration 8760h Last commit cache TTL (1 year)
COMMITS_COUNT int64 1000 Commits count threshold for caching

[camo] Section

Key Type Default Description
ENABLED bool false Enable camo image proxy
SERVER_URL string Camo server URL (required if enabled)
HMAC_KEY string HMAC key for camo (required if enabled)
ALWAYS bool false Proxy all images (not just HTTP)

[mailer] Section

Key Type Description
ENABLED bool Enable email sending
SMTP_ADDR string SMTP server address
SMTP_PORT int SMTP server port
FROM string Sender email address
USER string SMTP authentication username
PASSWD string SMTP authentication password

[storage] Section

Gitea supports pluggable storage backends for attachments, LFS, avatars, packages, and action artifacts.

Key Type Default Description
STORAGE_TYPE string local Default storage: local, minio
PATH string varies Local storage path
MINIO_ENDPOINT string MinIO/S3 endpoint
MINIO_ACCESS_KEY_ID string S3 access key
MINIO_SECRET_ACCESS_KEY string S3 secret key
MINIO_BUCKET string gitea S3 bucket name
MINIO_LOCATION string S3 region
MINIO_USE_SSL bool false Use SSL for S3
SERVE_DIRECT bool false Redirect to S3 URLs directly

Storage inheritance: specific storage sections ([storage.attachments], [storage.lfs], etc.) inherit from [storage] unless overridden.

Tip

You can define named storage backends and reference them:

[storage.my_s3]
STORAGE_TYPE = minio
MINIO_ENDPOINT = s3.example.com

[attachment]
STORAGE_TYPE = my_s3

Key Environment Variables

Variable Purpose
GITEA_RUN_MODE Run mode override (dev, prod, test)
GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT Suppress root user warning
GITEA_RUNNER_REGISTRATION_TOKEN Auto-register runner token
GITEA_RUNNER_REGISTRATION_TOKEN_FILE Read runner token from file
SKIP_MINWINSVC Skip Windows service detection

4. Deployment & Scaling

Docker Deployment

docker-compose.yml (PostgreSQL):

version: "3"
services:
  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    environment:
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=db:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=gitea
      - GITEA__database__PASSWD=gitea
    restart: always
    volumes:
      - ./gitea/data:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "2222:22"
    depends_on:
      - db

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

Rootless Docker variant: Use gitea/gitea:latest-rootless for environments that prohibit running as root. The rootless image uses different default paths:

Standard Image Rootless Image
/data /var/lib/gitea (data), /etc/gitea (config)
Runs as root, drops to git Runs as uid 1000 throughout
SSH on port 22 SSH on port 2222

Warning

The rootless image cannot bind to privileged ports (< 1024). Map to higher ports (e.g., -p 2222:2222 for SSH, -p 3000:3000 for HTTP).

Systemd Service

[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target
After=postgresql.service

[Service]
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
Restart=always
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea

[Install]
WantedBy=multi-user.target

Supervisor Configuration

[program:gitea]
directory=/var/lib/gitea/
command=/usr/local/bin/gitea web --config /etc/gitea/app.ini
autostart=true
autorestart=true
startsecs=10
stdout_logfile=/var/log/gitea/stdout.log
stderr_logfile=/var/log/gitea/stderr.log
environment=USER="git",HOME="/home/git"
user=git

macOS (launchd)

Place a plist in ~/Library/LaunchAgents/ or /Library/LaunchDaemons/ referencing the Gitea binary.

Reverse Proxy Configuration

Nginx:

server {
    listen 443 ssl;
    server_name git.example.com;

    ssl_certificate     /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    location / {
        proxy_pass http://127.0.0.1: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;
        client_max_body_size 100m;
    }
}

Note

When behind a reverse proxy, set [server] ROOT_URL to the external URL (e.g., https://git.example.com/) and ensure the proxy forwards the Host, X-Real-IP, and X-Forwarded-Proto headers.

Snap Deployment

sudo snap install gitea

The snap package automatically manages service lifecycle. Configuration is at /var/snap/gitea/common/conf/app.ini.

Scaling Considerations

Gitea is designed as a single-instance application. For high availability:

  • Database: Use a replicated PostgreSQL or MySQL cluster
  • Storage: Use S3-compatible object storage for attachments, LFS, packages, and artifacts
  • Cache: Use Redis for shared caching across potential future multi-instance setups
  • Queue: Use Redis-backed queues for reliable job processing
  • Search: Use Elasticsearch or Meilisearch for external indexing

5. Operations

CLI Command Reference

Gitea provides a comprehensive CLI for administration:

Command Purpose
gitea web Start the web server (default command)
gitea admin user list List users
gitea admin user create Create a user
gitea admin user change-password Change user password
gitea admin user delete Delete a user
gitea admin auth list List authentication sources
gitea admin auth add-ldap Add LDAP (Bind DN) authentication
gitea admin auth update-ldap Update LDAP (Bind DN) authentication
gitea admin auth add-ldap-simple Add LDAP (simple auth) authentication
gitea admin auth update-ldap-simple Update LDAP (simple auth) authentication
gitea admin auth add-oauth Add OAuth2 authentication
gitea admin auth update-oauth Update OAuth2 authentication
gitea admin auth add-smtp Add SMTP authentication
gitea admin auth update-smtp Update SMTP authentication
gitea admin auth delete Delete authentication source
gitea admin repo-sync-releases Sync releases with Git tags
gitea admin regenerate hooks Regenerate Git hooks
gitea admin regenerate keys Regenerate SSH keys
gitea admin sendmail Send email to all users
gitea migrate Run database migrations
gitea doctor check Run health checks
gitea doctor check --fix Run health checks and fix issues
gitea manager Runtime management (flush queues, shutdown, etc.)
gitea dump Create backup archive
gitea dump-repository Dump repository data
gitea restore-repository Restore repository from dump
gitea embedded list List embedded resources
gitea embedded extract Extract embedded resources
gitea migrate-storage Migrate between storage backends
gitea actions generate-runner-token Generate runner registration token
gitea cert Generate self-signed TLS certificate
gitea generate secret Generate secrets (INTERNAL_TOKEN, JWT_SECRET, etc.)
gitea config Display resolved configuration
gitea docs Generate CLI documentation

Tip

Add --help to any command for usage details and available flags. The gitea config command is standalone and does not require a running instance or database connection.

Backup and Restore

Create a full backup:

gitea dump --type zip --file /backup/gitea-$(date +%Y%m%d).zip

The dump includes: database, repositories, configuration, custom files, log, and data directories.

Restore procedure:

  1. Stop Gitea
  2. Extract the dump archive
  3. Restore the database from the SQL dump
  4. Copy repositories and data directories to the correct paths
  5. Restore app.ini configuration
  6. Start Gitea and run gitea migrate if upgrading

Note

gitea dump-repository and gitea restore-repository are separate commands for individual repository export/import, useful for migration between instances.

Monitoring

Gitea exposes Prometheus metrics at /metrics when enabled:

[metrics]
ENABLED = true
TOKEN   = your-metrics-token

Key metrics exposed:

  • gitea_accesses — number of access operations
  • gitea_actions — CI/CD action counts
  • gitea_issues — issue counts by state
  • gitea_repositories — repository counts
  • gitea_users — user counts by type

A Grafana monitoring mixin is provided in contrib/gitea-monitoring-mixin/ with pre-built dashboards.

Tip

Use the health endpoint at /api/v1/health for basic liveness checks. It does not require authentication.

Database Maintenance

Run pending migrations:

gitea migrate

Supported database operations:

  • Automatic schema migration on version upgrades
  • XORM handles cross-database SQL generation
  • Per-database atomic resource indexing (PostgreSQL uses ON CONFLICT ... RETURNING, MySQL uses ON DUPLICATE KEY UPDATE, MSSQL uses MERGE)

Queue System

Gitea uses an internal queue system for asynchronous processing (webhooks, indexing, notifications, etc.). Queue backends:

Backend Configuration Best For
channel In-memory Go channels Development, small instances
level LevelDB on disk Single-instance production
redis Redis connection High-availability setups

Cron Jobs

Gitea includes built-in cron jobs managed via the [cron] configuration section:

  • Repository health checks
  • Mirror synchronization
  • Archive cleanup
  • Statistics generation
  • Indexer flushing

These are managed by the gocron scheduler and can be configured or disabled via app.ini.

6. Performance

Search Indexer Options

Indexer Config Performance Profile
bleve (default) [indexer] ISSUE_INDEXER_TYPE = bleve Good for small/medium instances
Elasticsearch [indexer] ISSUE_INDEXER_TYPE = elasticsearch Better for large instances (100K+ issues)
Meilisearch [indexer] ISSUE_INDEXER_TYPE = meilisearch Fast, lightweight alternative
None [indexer] REPO_INDEXER_ENABLED = false Disable code search to save resources

Cache Configuration

  • Memory cache (default): Zero configuration, lost on restart
  • Redis: Shared cache, persistent, recommended for production
  • Memcache: Alternative shared cache
  • TwoQueue: Memory-based with eviction policies
[cache]
ADAPTER  = redis
HOST     = redis://127.0.0.1:6379/0
ITEM_TTL = 16h

Git Performance

  • Gitea uses the commit-graph feature for faster Git operations when available
  • The [git] section allows tuning Git operation timeouts
  • Large repository support can be improved by enabling commit caching

7. Security

Authentication Methods

Method Type Configuration
Local Built-in Default, no additional config
LDAP (Bind DN) External gitea admin auth add-ldap
LDAP (Simple Auth) External gitea admin auth add-ldap-simple
OAuth2 External gitea admin auth add-oauth (GitHub, GitLab, Google, etc.)
SMTP External gitea admin auth add-smtp
PAM External Build with pam tag
SSPI External Windows integrated auth (build with sspi tag, Windows only)
WebAuthn/FIDO2 2FA User-enabled via Settings > Security
TOTP 2FA User-enabled via Settings > Security

Warning

LDAP bind passwords are stored in the database. Use a dedicated service account with minimal privileges for LDAP authentication. The [admin] section's USER_DISABLED_FEATURES can restrict what external auth users can modify.

OAuth2 Provider

Gitea can act as an OAuth2 authorization server, allowing third-party applications to authenticate against your Gitea instance. Configure OAuth2 applications in Site Administration > Applications.

CAPTCHA Support

Gitea supports multiple CAPTCHA providers to prevent automated registration:

Provider Configuration Key
reCAPTCHA [service] ENABLE_CAPTCHA = true, [recaptcha] section
hCaptcha [service] ENABLE_CAPTCHA = true, [hcaptcha] section
mCaptcha [service] ENABLE_CAPTCHA = true, [mcaptcha] section
Cloudflare Turnstile [service] ENABLE_CAPTCHA = true, [turnstile] section

Secret Management

Secret Purpose Generation
INTERNAL_TOKEN Internal API authentication gitea generate secret INTERNAL_TOKEN
JWT_SECRET JWT token signing gitea generate secret JWT_SECRET
LFS_JWT_SECRET LFS JWT signing gitea generate secret LFS_JWT_SECRET
SECRET_KEY Global secret key gitea generate secret SECRET_KEY

Caution

Never expose INTERNAL_TOKEN or database credentials. The internal API (routers/private/) is used for SSH-to-web communication and has elevated privileges. It should never be exposed to external networks.

Signing Support

Gitea supports GPG and SSH signing for commits and tags:

  • Sign commits using GPG keys managed in User Settings > SSH / GPG Keys
  • Gitea can verify commit signatures and display verification status
  • SSH keys from LDAP can be automatically flagged as verified via --ssh-keys-are-verified

Host Access Control

The modules/hostmatcher package provides URL/host allowlisting and denylisting for outgoing requests (webhooks, migrations, etc.) to prevent SSRF attacks.

8. Developer Internals

Repository Structure

gitea/
├── main.go                  # Entry point, version info, CLI dispatch
├── cmd/                     # CLI commands (52 files, 282K chars)
│   ├── main.go              # Command tree: web, admin, doctor, dump, etc.
│   ├── web.go               # Web server startup
│   ├── admin.go             # Admin subcommands
│   ├── doctor.go            # Health check system
│   └── ...
├── models/                  # Data layer (XORM models)
│   ├── db/                  # Database engine, migrations infrastructure
│   ├── user/                # User model (28 files)
│   ├── repo/                # Repository model (47 files)
│   ├── issues/              # Issue/PR model (47 files, 460K chars — largest)
│   ├── actions/             # CI/CD action models
│   ├── auth/                # Authentication source models
│   ├── packages/            # Package registry models
│   ├── git/                 # Git-specific models (branches, commits, LFS)
│   ├── organization/        # Organization model
│   ├── project/             # Project board model
│   ├── webhook/             # Webhook models
│   └── migrations/          # Database schema migrations
├── modules/                 # Shared libraries
│   ├── setting/             # Configuration system (75 files, 294K chars)
│   ├── git/                 # Git operations (113 files, 333K chars)
│   ├── markup/              # Markdown/Org rendering
│   ├── storage/             # Storage abstraction (local, S3, Azure)
│   ├── queue/               # Async queue system
│   ├── indexer/             # Search indexer (bleve, ES, Meilisearch)
│   ├── cache/               # Caching layer
│   ├── log/                 # Logging system
│   ├── structs/             # API request/response structs (55 files)
│   ├── graceful/            # Graceful shutdown manager
│   ├── actions/             # Actions workflow support
│   ├── packages/            # Package format parsers
│   └── ...
├── routers/                 # HTTP routing
│   ├── web/                 # Web UI routes (go-chi)
│   ├── api/                 # REST API + package registry
│   ├── private/             # Internal API (SSH ↔ web)
│   └── install/             # Installation wizard
├── services/                # Business logic
│   ├── pull/                # Pull request operations (31 files, 220K chars)
│   ├── repository/          # Repository management (38 files, 245K chars)
│   ├── migrations/          # Migration from other forges (26 files, 273K chars)
│   ├── actions/             # CI/CD service layer (24 files, 163K chars)
│   ├── webhook/             # Webhook delivery (26 files, 263K chars)
│   ├── context/             # Request context (27 files, 124K chars)
│   ├── issue/               # Issue operations
│   ├── auth/                # Authentication service
│   ├── mailer/              # Email delivery
│   ├── forms/               # Form validation
│   └── ...
├── templates/               # Go HTML templates
├── web_src/                 # Frontend source
│   ├── js/                  # JavaScript/TypeScript
│   ├── css/                 # Stylesheets
│   └── fomantic/            # Fomantic UI customization
├── options/                 # Embedded resources
│   ├── locale/              # 28 locale files (5.2M chars)
│   ├── license/             # License templates
│   ├── gitignore/           # .gitignore templates
│   └── label/               # Issue label presets
├── contrib/                 # Service manager configs, IDE configs
├── docker/                  # Dockerfile and rootless variant
├── snap/                    # Snap package definition
└── tests/                   # Integration, e2e, fuzz tests

Architecture Deep Dive

Request lifecycle:

  1. HTTP request arrives at go-chi router (routers/web/web.go or routers/api/)
  2. Middleware chain: logging, context setup, auth, CSRF, session
  3. Handler calls service layer (services/)
  4. Service calls model layer (models/) for data access
  5. Models use XORM to execute database queries
  6. Response rendered via Go templates (templates/) or JSON

Key design patterns:

  • Service context: services/context provides a rich request context wrapping chi's context with user info, repository info, permissions, and flash messages
  • Notification system: services/notify implements a fan-out notification pattern — actions trigger notifications that are dispatched to multiple handlers (email, webhook, UI, indexer)
  • Storage abstraction: modules/storage provides a unified interface for file storage, with implementations for local filesystem, MinIO/S3, and Azure Blob Storage
  • Queue system: modules/queue implements async job processing with multiple backends (channel, level, redis)

Module Reference

modules/setting (75 files, 294K chars)

The configuration system. Each config section has its own file (e.g., actions.go, database.go, server.go). Settings are loaded from INI files via gopkg.in/ini.v1, with environment variable overrides applied via the GITEA__ prefix mechanism in config_env.go.

modules/git (113 files, 333K chars)

Git operations wrapper. Provides Go interfaces for Git commands, including repository operations, commit parsing, diff generation, and LFS support. Uses native Git CLI commands via os/exec with structured error handling.

models/issues (47 files, 460K chars)

The largest model package. Covers issues, pull requests, comments, labels, milestones, reactions, time tracking, and issue dependencies. Uses XORM for database access with per-database optimized queries.

services/pull (31 files, 220K chars)

Pull request business logic: creation, merging (merge, rebase, squash, fast-forward, manually merged), auto-merge scheduling, update branch, code review, and conflict detection.

services/webhook (26 files, 263K chars)

Webhook delivery system supporting multiple services (Gitea, Gogs, Slack, Discord, Telegram, Matrix, Microsoft Teams, Feishu, DingTalk, WeCom, Packagist). Each service has its own payload formatter.

services/migrations (26 files, 273K chars)

Repository migration from external platforms. Each platform has a downloader implementation: GitHub, GitLab, Gitea, Gogs, Bitbucket, OneDev, CodeCommit, Codebase.

services/actions (24 files, 163K chars)

Gitea Actions CI/CD backend. Handles workflow parsing, job queuing, runner communication (via gRPC/connect-rpc), artifact storage, and log management.

routers/api (REST API)

The Swagger-documented REST API provides programmatic access to all Gitea features. API routes are defined with go-swagger annotations and compiled into templates/swagger/v1_json.tmpl. Key API areas:

  • /api/v1/repos/ — Repository CRUD and management
  • /api/v1/user/ — User operations
  • /api/v1/orgs/ — Organization management
  • /api/v1/admin/ — Admin operations
  • /api/v1/packages/ — Package registry API
  • /api/v1/settings/ — Instance settings

Code Patterns and Conventions

Naming:

  • Copyright headers: // Copyright YYYY The Gitea Authors. All rights reserved. with SPDX license identifier
  • Go packages follow standard naming (lowercase, no underscores)
  • Test files use _test.go suffix

Error handling:

  • Structured error types in modules/git/error.go and domain-specific error types
  • Errors wrap with fmt.Errorf("context: %w", err) for stack context
  • The runStdError type captures Git command stderr for debugging

Database:

  • XORM ORM with per-database optimized queries (PostgreSQL, MySQL, SQLite, MSSQL)
  • Atomic resource index via database-specific upsert patterns
  • Schema migrations in models/migrations/ with version tracking
  • WAL mode for SQLite, FK enforcement

Linting:

  • golangci-lint with strict configuration (.golangci.yml)
  • Custom rules: no encoding/json (use modules/json), no gopkg.in/ini.v1 (use config system), no io/ioutil
  • Go header check enforcing copyright format
  • eslint for TypeScript/JavaScript with comprehensive rule set

Build system:

  • Makefile-driven (make build, make test-sqlite, make test-pgsql, make test-mysql, make test-mssql)
  • Build tags: bindata (embed assets), sqlite (SQLite support), pam (PAM auth)
  • Frontend: Vite bundler, TypeScript, ESLint
  • air for hot-reload development

Testing Strategy

Test types:

Type Location Command
Unit tests *_test.go alongside source go test ./...
Integration tests tests/integration/ make test-sqlite, make test-pgsql, make test-mysql, make test-mssql
E2E tests tests/e2e/ Playwright-based browser tests
Fuzz tests tests/fuzz/ Go fuzz testing

Integration test databases:

# SQLite (simplest)
make test-sqlite

# PostgreSQL (via Docker)
docker run -e "POSTGRES_DB=test" -e "POSTGRES_USER=postgres" -e "POSTGRES_PASSWORD=postgres" -p 5432:5432 --rm postgres:latest
TEST_PGSQL_HOST=localhost:5432 TEST_PGSQL_DBNAME=postgres TEST_PGSQL_USERNAME=postgres TEST_PGSQL_PASSWORD=postgres make test-pgsql

# MySQL (via Docker)
docker run -e "MYSQL_DATABASE=test" -e "MYSQL_ALLOW_EMPTY_PASSWORD=yes" -p 3306:3306 --rm mysql:latest
TEST_MYSQL_HOST=localhost:3306 TEST_MYSQL_DBNAME=test TEST_MYSQL_USERNAME=root make test-mysql

# MSSQL (via Docker)
docker run -e "ACCEPT_EULA=Y" -e "MSSQL_PID=Standard" -e "SA_PASSWORD=MwantsaSecurePassword1" -p 1433:1433 --rm microsoft/mssql-server-linux:latest
TEST_MSSQL_HOST=localhost:1433 TEST_MSSQL_DBNAME=gitea_test TEST_MSSQL_USERNAME=sa TEST_MSSQL_PASSWORD=MwantsaSecurePassword1 make test-mssql

Running individual tests:

make test-sqlite#GPG

Test environment variables:

Variable Purpose
GITEA_TEST_SLOW_RUN Threshold for slow test warning (default: 10s)
GITEA_TEST_SLOW_FLUSH Threshold for slow flush warning (default: 1s)
GITEA_TEST_LOG_SQL Enable SQL query logging in tests
GITEA_TEST_E2E Enable E2E test mode

Running tests via local act_runner:

# All jobs (resource-intensive, not recommended)
act_runner exec -W ./.github/workflows/pull-db-tests.yml --event=pull_request --default-actions-url="https://github.com" -i catthehacker/ubuntu:runner-latest

# Single job
act_runner exec -W ./.github/workflows/pull-db-tests.yml --event=pull_request --default-actions-url="https://github.com" -i catthehacker/ubuntu:runner-latest -j <job_name>

# List all jobs
act_runner exec -W ./.github/workflows/pull-db-tests.yml --event=pull_request --default-actions-url="https://github.com" -i catthehacker/ubuntu:runner-latest -l

Doctor System

The gitea doctor command (services/doctor/) provides automated health checking and repair:

  • Database consistency checks
  • Repository integrity verification
  • SSH authorized_keys file synchronization
  • Git hook file verification
  • Permission checks

Potential Improvements

  • models/issues (460K chars, 47 files) — the largest model package — could benefit from decomposition
  • The REST API is defined via Swagger annotations spread across many files; centralized API versioning is implicit
  • Frontend uses a mix of vanilla JS, jQuery-era patterns (Fomantic UI), and modern Vue.js — a gradual migration toward Vue components is ongoing
  • Configuration (75 files) covers many sections but lacks a formal schema validation layer

Contributing

Quick setup:

git clone https://github.com/go-gitea/gitea.git
cd gitea
make deps-frontend deps-backend
TAGS="bindata sqlite sqlite_unlock_notify" make build
./gitea web

Development hot-reload:

# Uses air (.air.toml) for automatic rebuild
air

Code style: Enforced by golangci-lint and eslint. Run make lint-backend lint-frontend before committing.

PR labels used for changelog:

Label Category
pr/breaking BREAKING
topic/security SECURITY
type/feature FEATURES
type/enhancement ENHANCEMENTS
type/bug BUGFIXES
modifies/api API
type/testing TESTING
type/docs DOCS

Translations are managed via Crowdin — do not edit locale files directly.

9. Integration Points

REST API

Full Swagger documentation at {ROOT_URL}/api/swagger. The API supports:

  • Token-based authentication (personal access tokens, OAuth2 tokens)
  • Pagination via page and limit query parameters
  • Default page size: 30, max items: 50 (configurable)

Webhook Events

Gitea fires webhooks for repository events:

  • create, delete, fork, push, issues, issue_assign, issue_label, issue_milestone, issue_comment
  • pull_request, pull_request_assign, pull_request_label, pull_request_milestone, pull_request_comment, pull_request_review, pull_request_sync
  • repository, release, package, wiki, pull_request_review_request

Each webhook delivery includes: event type, delivery ID, payload JSON, sender information, and signature (HMAC-SHA256).

Package Registry

Gitea's package registry implements standard protocols for 20+ package formats. Each format lives in its own module:

  • Models: models/packages/ + models/packages/<type>/
  • Parser: modules/packages/<type>/
  • Router: routers/api/packages/<type>/
  • Service: services/packages/ + services/packages/<type>/

Go Modules (go-get)

Gitea serves go-get meta tags for Go module discovery. When a ?go-get=1 query parameter is present, Gitea returns HTML with <meta name="go-import"> and <meta name="go-source"> tags pointing to the repository.

SSH Integration

Gitea supports two SSH modes:

  1. Built-in SSH server ([server] START_SSH_SERVER = true): Uses github.com/gliderlabs/ssh
  2. OpenSSH integration: Gitea manages the authorized_keys file and uses the gitea serv command as the shell for Git operations

The cmd/hook.go processes Git hooks (pre-receive, update, post-receive) to enforce branch protections, trigger webhooks, and update indexes.

Migration Protocols

Platform Downloader Features
GitHub services/migrations/github.go Repos, issues, PRs, labels, milestones, releases, wiki, comments
GitLab services/migrations/gitlab.go Repos, issues, PRs, labels, milestones, releases, wiki, comments
Gitea services/migrations/gitea_downloader.go Full bidirectional migration
Gogs services/migrations/gogs.go Repos, issues, labels, milestones
Bitbucket services/migrations/bitbucket.go Repos, issues, PRs
OneDev services/migrations/onedev.go Repos, issues, PRs
CodeCommit services/migrations/codecommit.go Repos
Codebase services/migrations/codebase.go Repos, issues

Prometheus Metrics

Available at /metrics when [metrics] ENABLED = true. Provides standard Go runtime metrics plus Gitea-specific counters. A Grafana mixin (contrib/gitea-monitoring-mixin/) provides ready-made dashboards.

IDE Integration

Gitea supports "Open With" external editors. Default configured editors:

Editor URL Scheme
VS Code vscode://vscode.git/clone?url={url}
VSCodium vscodium://vscode.git/clone?url={url}
IntelliJ IDEA jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo={url}

Custom editors can be configured in [repository.editor] or via the admin panel.

VS Code workspace settings are provided in contrib/ide/vscode/ for contributors.

10. Troubleshooting

Error: Stuck database migrations

Cause: Migration was interrupted during a schema change.

Solution:

  1. Check version table in the database for current migration version
  2. Manually investigate the failed migration in models/migrations/
  3. Apply the migration manually if needed, then update the version

Warning

Never manually edit migration version numbers without understanding the schema change. Always back up the database before attempting manual migration fixes.

Error: SSH operations fail with permission denied

Cause: SSH key not properly registered, or authorized_keys file is out of sync.

Solution:

  1. Run gitea admin regenerate keys to rebuild the authorized_keys file
  2. Verify the git user's home directory has correct permissions
  3. Check that gitea serv is correctly configured as the SSH shell

Error: High memory usage

Cause: Large repository operations, indexer, or unbounded caching.

Solution:

  1. Monitor queue depths via gitea manager or Prometheus metrics
  2. Tune [cache] ITEM_TTL and consider using Redis
  3. Reduce [repository] MAX_CREATION_LIMIT if needed
  4. Consider disabling repository code indexing for very large instances

Tip

The gitea manager command provides runtime management capabilities including flushing queues, dumping goroutine stacks, and graceful shutdown — useful for diagnosing production issues without restarting.

Error: Webhook delivery timeouts

Cause: Target service is slow or unreachable.

Solution:

  1. Check webhook delivery logs in the admin panel
  2. Increase timeout settings if the target is legitimately slow
  3. Verify network connectivity from the Gitea server
  4. Check for TLS issues if using HTTPS webhook targets

Error: Flaky LDAP authentication

Cause: Network instability, LDAP server load, or configuration issues (go-gitea/gitea#37263).

Solution:

  1. Verify LDAP server is responsive: ldapsearch -H ldap://server -x -b "dc=example,dc=com"
  2. Check the security protocol setting matches the server configuration
  3. Verify the bind DN has read access to the user search base
  4. Enable debug logging for auth operations