Gitea
Lightweight self-hosted Git service — deployment, CI/CD, authentication & operations
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
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.
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.
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.
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.
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.comfor your OS/architecture - Make it executable:
chmod +x gitea - Run the initial setup wizard at
http://localhost:3000/install - Use
systemdto 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;
}
}
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).
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
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.
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
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.
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
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.
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
| Source | Repos | Issues | PRs | Wikis | Labels | Milestones | Releases |
|---|---|---|---|---|---|---|---|
| GitHub | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| GitLab | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| Bitbucket | Yes | Yes | Yes | No | No | Yes | No |
| Gogs | Yes | Yes | No | Yes | Yes | Yes | No |
| OneDev | Yes | Yes | No | No | Yes | Yes | No |
| Plain Git URL | Yes | No | No | No | No | No | No |
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
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.
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 gcperiodically 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
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.
Gitea vs GitLab vs Forgejo
Understanding how Gitea compares to alternatives helps position it correctly for different use cases.
Feature comparison
| Feature | Gitea | GitLab CE | Forgejo |
|---|---|---|---|
| Language | Go | Ruby + Go | Go (fork of Gitea) |
| Min RAM | ~256 MB | ~4 GB | ~256 MB |
| Single binary | Yes | No (Omnibus) | Yes |
| Built-in CI/CD | Gitea Actions | GitLab CI/CD | Forgejo Actions |
| Container registry | Package registry (OCI) | Full registry | Package registry (OCI) |
| Security scanning | No | SAST, DAST, dependency | No |
| Project management | Issues, projects (basic) | Issues, epics, boards | Issues, projects (basic) |
| Pages (static sites) | No (use CI deploy or third-party) | GitLab Pages | No built-in (third-party via Codeberg Pages) |
| License | MIT | MIT (CE) | GPL-3.0+ (copyleft, since v9.0) |
| Governance | Company-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
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.
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_URLto 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_KEYandINTERNAL_TOKENto strong random values - Use the rootless Docker image (
gitea/gitea:*-rootless) - Enable HTTPS and HSTS at the reverse proxy level
- Configure
[session] COOKIE_SECURE = trueand[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 = trueinapp.ini) - Deploy at least one act_runner and register it with the instance
- Configure runner labels to match workflow
runs-onvalues - 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_dumporgitea 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