Tutorial

Self-Host Gitea: A Lightweight GitHub Alternative on Your Server

May 03, 2026

Back to Blog

Why Self-Host Your Git Platform?

GitHub is the center of the open-source universe, and for good reason — it is polished, reliable, and packed with features. But there are compelling reasons to run your own Git hosting platform: your code stays on your hardware, you control access completely, there are no per-user fees for private repositories, and you are not dependent on a third-party service that could change pricing, terms, or availability at any time.

Gitea has emerged as the most popular self-hosted Git solution for teams who want a GitHub-like experience without the resource overhead of GitLab. Written in Go and compiled to a single binary, Gitea starts in under a second, uses roughly 100MB of RAM, and provides a feature set that covers 90% of what most teams need from a Git platform.

What is Gitea? Gitea is an open-source, community-managed, lightweight Git hosting solution. It is a fork of Gogs (Go Git Service) with active development, better security, and more features. The name "Gitea" is a play on "Git" with a cup of "tea."

Gitea vs GitHub vs GitLab: Resource Comparison

The most striking difference between these platforms is resource consumption. GitLab is powerful but notoriously heavy. Gitea is designed to be the opposite — lean and fast.

MetricGitHub (SaaS)GitLab Self-HostedGitea Self-Hosted
RAM Usage (idle)N/A (SaaS)~4 GB minimum~100 MB
Recommended RAMN/A8+ GB512 MB
CPU CoresN/A4+ recommended1 is enough
Disk (base install)N/A~2.5 GB~100 MB
Start TimeN/A2-5 minutes< 1 second
Docker Image SizeN/A~1.5 GB~100 MB
Private ReposUnlimited (free)UnlimitedUnlimited
Built-in CI/CDGitHub ActionsGitLab CIGitea Actions
Cost (10 users)Free or $4/user/moFree (CE)Free
100 MB
Gitea idle RAM usage
4+ GB
GitLab idle RAM usage

What Gitea Offers

Despite its small footprint, Gitea is not a toy. It includes a comprehensive feature set:

  • Git hosting with HTTP and SSH protocols
  • Pull requests with code review, approvals, and merge options
  • Issue tracker with labels, milestones, and project boards
  • Gitea Actions — GitHub Actions-compatible CI/CD workflows
  • Container registry for Docker images
  • Package registry for npm, Maven, PyPI, NuGet, and more
  • Wiki per repository
  • Organizations and teams with granular permissions
  • Repository mirroring (push and pull mirrors)
  • Webhooks for integration with external services
  • OAuth2/OpenID Connect authentication
  • Two-factor authentication
  • LFS support for large files
  • GPG signed commits verification

Step-by-Step Deployment with Docker Compose

Step 1: Create the Project Directory

$ mkdir -p ~/gitea && cd ~/gitea

Step 2: Write the Docker Compose File

# docker-compose.yml services: gitea: image: gitea/gitea:latest restart: unless-stopped depends_on: db: condition: service_healthy environment: - USER_UID=1000 - USER_GID=1000 - GITEA__database__DB_TYPE=postgres - GITEA__database__HOST=db:5432 - GITEA__database__NAME=gitea - GITEA__database__USER=gitea - GITEA__database__PASSWD=${DB_PASSWORD} - GITEA__server__DOMAIN=${DOMAIN} - GITEA__server__ROOT_URL=https://${DOMAIN}/ - GITEA__server__SSH_DOMAIN=${DOMAIN} - GITEA__server__SSH_PORT=2222 - GITEA__server__LFS_START_SERVER=true - GITEA__service__DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-false} - GITEA__service__REQUIRE_SIGNIN_VIEW=true - GITEA__mailer__ENABLED=false volumes: - gitea_data:/data - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro ports: - "3000:3000" - "2222:22" networks: - frontend - backend db: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: gitea POSTGRES_USER: gitea POSTGRES_PASSWORD: ${DB_PASSWORD} volumes: - db_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U gitea"] interval: 10s timeout: 5s retries: 5 networks: - backend volumes: gitea_data: db_data: networks: frontend: backend: internal: true

Step 3: Configure Environment Variables

# .env DB_PASSWORD=your_strong_database_password_here DOMAIN=git.yourdomain.com DISABLE_REGISTRATION=false
Registration Control: Set DISABLE_REGISTRATION=true after you create the admin account. This prevents anyone from creating accounts on your Gitea instance. You can always create accounts manually from the admin panel.

Step 4: Launch and Configure

$ docker compose up -d [+] Running 4/4 Network gitea_backend Created Network gitea_frontend Created Container gitea-db-1 Healthy Container gitea-gitea-1 Started # Check it's running $ docker compose ps NAME SERVICE STATUS PORTS gitea-db-1 db running 5432/tcp gitea-gitea-1 gitea running 0.0.0.0:3000->3000, 0.0.0.0:2222->22 # Follow startup logs $ docker compose logs -f gitea ... Server listening on 0.0.0.0:3000 ...

Open http://your-server-ip:3000 in your browser. The first time you access Gitea, you will see the installation page where you can create the admin account. All database settings are pre-configured from the environment variables.

Setting Up the Reverse Proxy

For production use, you should run Gitea behind a reverse proxy with SSL. Here is an nginx configuration:

# /etc/nginx/sites-available/gitea server { listen 80; server_name git.yourdomain.com; return 301 https://$host$request_uri; } server { listen 443 ssl http2; server_name git.yourdomain.com; ssl_certificate /etc/letsencrypt/live/git.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/git.yourdomain.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; client_max_body_size 512M; 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; } }
# Get SSL certificate $ sudo certbot --nginx -d git.yourdomain.com # Test the configuration $ sudo nginx -t && sudo systemctl reload nginx

SSH Key Management

Git over SSH is faster and more convenient than HTTPS for daily development. Gitea supports SSH keys natively.

1
Generate an SSH key on your development machine (if you do not have one already):
$ ssh-keygen -t ed25519 -C "[email protected]"
2
Add the public key to Gitea: Settings > SSH/GPG Keys > Add Key. Paste the contents of ~/.ssh/id_ed25519.pub.
3
Configure your SSH client to use the custom port:
# ~/.ssh/config Host git.yourdomain.com Port 2222 User git IdentityFile ~/.ssh/id_ed25519
4
Clone using SSH:
$ git clone [email protected]:myorg/myrepo.git

CI/CD with Gitea Actions

Gitea Actions is compatible with GitHub Actions workflows. If you have existing GitHub Actions YAML files, they will work with Gitea with minimal or no modification.

Setting Up the Gitea Runner

# Generate a runner registration token from Gitea admin # Site Administration > Actions > Runners > Create new runner # Add the runner to docker-compose.yml runner: image: gitea/act_runner:latest restart: unless-stopped depends_on: - gitea environment: GITEA_INSTANCE_URL: http://gitea:3000 GITEA_RUNNER_REGISTRATION_TOKEN: ${RUNNER_TOKEN} GITEA_RUNNER_NAME: local-runner volumes: - /var/run/docker.sock:/var/run/docker.sock - runner_data:/data networks: - frontend

Example Workflow File

# .gitea/workflows/ci.yml name: CI Pipeline on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: go-version: '1.22' - run: go test ./... - run: go build -o /dev/null ./... lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: golangci/golangci-lint-action@v4
GitHub Actions Compatibility: Gitea Actions supports the same workflow syntax as GitHub Actions. Most uses: actions from the GitHub marketplace work directly. You can migrate existing workflows with zero or minimal changes.

Repository Mirroring

Gitea supports both push mirrors (your Gitea repo pushes to GitHub/GitLab) and pull mirrors (Gitea pulls from GitHub/GitLab). This is invaluable for maintaining synchronized copies across platforms.

Pull Mirror

Create a mirror of a GitHub repo that automatically stays in sync. Perfect for local caching or as a backup.

Settings > Repository > Mirror Settings > Pull from remote

Push Mirror

Automatically push your Gitea repos to GitHub or GitLab. Great for maintaining a public mirror while developing privately.

Settings > Repository > Mirror Settings > Push to remote

# Create a mirror of a GitHub repository via API $ curl -X POST https://git.yourdomain.com/api/v1/repos/migrate \ -H "Authorization: token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "clone_addr": "https://github.com/user/repo.git", "mirror": true, "repo_name": "repo-mirror", "repo_owner": "myorg", "service": "github" }'

Organization and Team Management

For teams, Gitea provides GitHub-style organizations with fine-grained team permissions.

Organization
mycompany
Team: Owners
Full Access
Team: Developers
Write Access
Team: Reviewers
Read Access
Permission LevelReadWriteAdminUse For
ReadYesNoNoExternal reviewers, auditors
WriteYesYesNoActive developers
AdminYesYesYesTeam leads, project owners
OwnerYesYesYesOrganization administrators

Migrating from GitHub or GitLab

Gitea has built-in migration tools that import not just your code, but also issues, labels, milestones, releases, pull requests, and wikis.

1
Navigate to New Migration from the + menu in the top bar
2
Select the source platform (GitHub, GitLab, Bitbucket, Gitea, Gogs, OneDev, CodeCommit)
3
Enter the repository URL and an access token (for private repos)
4
Choose what to import: Issues, Pull Requests, Labels, Milestones, Releases, Wiki
5
Click Migrate Repository — Gitea handles the rest
# Bulk migration via API $ curl -X POST https://git.yourdomain.com/api/v1/repos/migrate \ -H "Authorization: token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "clone_addr": "https://github.com/user/repo.git", "auth_token": "ghp_YOUR_GITHUB_TOKEN", "repo_name": "repo", "repo_owner": "myorg", "service": "github", "issues": true, "labels": true, "milestones": true, "pull_requests": true, "releases": true, "wiki": true }'

Backup and Restore

Gitea includes a built-in backup command that creates a complete snapshot of your instance:

# Backup Gitea (inside the container) $ docker compose exec gitea gitea dump -c /data/gitea/conf/app.ini 2026/03/15 10:30:00 Creating dump archive gitea-dump-1710495000.zip 2026/03/15 10:30:02 Packing dump files... 2026/03/15 10:30:05 Finished: gitea-dump-1710495000.zip # Also backup the database separately $ docker compose exec db pg_dump -U gitea gitea | gzip > gitea-db-backup.sql.gz # Restore Gitea $ docker compose exec gitea gitea restore --file /data/gitea-dump-1710495000.zip
Backup Schedule: Set up a cron job to run daily backups. Keep at least 7 days of rotating backups. Store backups on a different server or cloud storage — a backup on the same disk as your data is not really a backup.

Webhook Integration

Gitea supports webhooks that can notify external services on repository events. This enables integration with chat platforms, CI/CD systems, and custom automation.

Built-in Webhook TargetsCustom Options
Gitea (cascade to other instances)Generic webhook (any HTTP endpoint)
SlackDiscord
Microsoft TeamsMatrix
TelegramFeishu (Lark)
DingtalkPackagist

Deploying Gitea with Panelica

Deploy Gitea on a Panelica-managed server with Docker, using the panel's built-in reverse proxy, SSL management, and database provisioning. The panel sets up the nginx reverse proxy for your Gitea domain automatically, provisions Let's Encrypt SSL certificates, and creates the database — turning the multi-step manual process into a streamlined deployment through the GUI. User access is managed through Panelica's RBAC system, and container resources are bounded by Cgroups v2 limits.

Performance Tips

1
Use PostgreSQL over SQLite: SQLite works for small installations, but PostgreSQL handles concurrent users and large repositories much better. Our Compose file already uses PostgreSQL.
2
Enable caching: Configure a memory cache in Gitea's settings to speed up frequently accessed data like user profiles and repository metadata.
3
SSH key optimization: For installations with many users, switch from the default authorized_keys file to the built-in SSH server or the Gitea SSH authorized_keys command.
4
LFS storage: For repositories with large binary files, Git LFS prevents your repo from becoming bloated. Gitea's built-in LFS server handles this transparently.
Resource Efficiency: Even with PostgreSQL, CI runners, and hundreds of repositories, a typical Gitea installation uses under 500MB of RAM. For comparison, GitLab CE recommends 8GB minimum and routinely uses 4GB+ at idle. If you are running on a VPS with limited resources, this difference is transformative.

Conclusion

Gitea proves that powerful software does not need to be resource-hungry. With 100MB of RAM, a sub-second startup time, and compatibility with GitHub Actions workflows, it delivers a complete Git hosting platform that can run on hardware as modest as a Raspberry Pi — or scale to serve organizations with hundreds of developers.

The setup we walked through gives you a production-ready Gitea instance with PostgreSQL for reliability, SSL for security, SSH for convenient access, and CI/CD through Gitea Actions. Add repository mirroring to keep a synchronized backup on GitHub, set up organizations for team access control, and configure webhooks for your deployment pipeline. You will have a self-hosted development platform that rivals SaaS offerings while keeping your code, your data, and your infrastructure entirely under your control.

The best part? If you ever outgrow Gitea's feature set, migrating away is as simple as pushing your repositories to any other Git host. There is no vendor lock-in, no proprietary format, just standard Git.

Share:
See the Demo