Gitea / Forgejo
Self-hosted Git service with web interface, install Gitea or Forgejo on your VPS with Docker
Gitea and Forgejo are lightweight, self-hosted Git services with a web interface. Forgejo is a community-maintained fork of Gitea. Both provide pull requests, issue tracking, wiki, and integrated CI/CD without the overhead of GitLab.
What is Gitea/Forgejo?
A self-hosted alternative to GitHub and GitLab for teams and personal projects. Runs efficiently on minimal resources, supports unlimited repositories, users, and collaborators. Forgejo is the recommended community continuation.
Docker Compose Installation
MySQL Database Setup
Create /etc/docker/compose/gitea/docker-compose.yml:
version: '3.8'
services:
db:
image: mysql:8.0
container_name: gitea_db
restart: always
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: gitea
MYSQL_USER: gitea
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- gitea_db:/var/lib/mysql
command: --default-authentication-plugin=mysql_native_password
gitea:
image: codeberg.org/forgejo/forgejo:latest
container_name: gitea
restart: always
depends_on:
- db
ports:
- "3000:3000"
- "222:22"
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=mysql
- GITEA__database__HOST=db:3306
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=${DB_PASSWORD}
- GITEA__server__DOMAIN=git.example.com
- GITEA__server__ROOT_URL=https://git.example.com
- GITEA__server__SSH_DOMAIN=git.example.com
- GITEA__server__SSH_PORT=222
- GITEA__service__DISABLE_REGISTRATION=false
volumes:
- gitea_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
volumes:
gitea_db:
gitea_data:Create .env file:
DB_ROOT_PASSWORD=your_secure_root_password
DB_PASSWORD=your_secure_gitea_passwordDeploy:
cd /etc/docker/compose/gitea
docker compose up -d
docker compose logs -f giteaPostgreSQL Alternative
For PostgreSQL instead of MySQL:
db:
image: postgres:15
container_name: gitea_db
restart: always
environment:
POSTGRES_DB: gitea
POSTGRES_USER: gitea
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- gitea_db:/var/lib/postgresql/dataUpdate environment variables:
environment:
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=${DB_PASSWORD}Initial Configuration
Access Gitea at http://your-ip:3000:
- Database configuration is auto-detected from environment variables
- Create admin account (username and password)
- Basic settings are applied
- Redirect to homepage and login
The web setup wizard should be skipped if all environment variables are set correctly.
SSH Configuration
SSH runs on port 222 to avoid conflict with system SSH on port 22.
For client SSH access, create ~/.ssh/config:
Host git.example.com
Hostname git.example.com
Port 222
User gitClone repositories:
git clone git@git.example.com:username/repo.gitNginx Reverse Proxy
Configure Nginx to proxy Gitea:
server {
listen 80;
server_name git.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name git.example.com;
ssl_certificate /etc/letsencrypt/live/git.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/git.example.com/privkey.pem;
client_max_body_size 100M;
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;
proxy_set_header X-Forwarded-Host $server_name;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
}
}Test and reload:
nginx -t
systemctl reload nginxConfiguration (app.ini)
For Docker, edit the mounted /data volume. Access the container:
docker compose exec gitea bashEdit /data/gitea/conf/app.ini:
Critical settings:
[server]
DOMAIN = git.example.com
ROOT_URL = https://git.example.com/
[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false
DEFAULT_KEEP_EMAIL_PRIVATE = true
[mailer]
ENABLED = true
SMTP_ADDR = smtp.example.com
SMTP_PORT = 587
USER = noreply@example.com
FROM = Gitea <noreply@example.com>
PASSWD = your_password
[security]
SECRET_KEY = generate_secure_key_with_openssl
INSTALL_LOCK = trueRestart Gitea after changes:
docker compose restart giteaActions (CI/CD)
Gitea includes built-in CI/CD similar to GitHub Actions.
Create .gitea/workflows/test.yml:
name: Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: |
npm install
npm testWorkflows execute in Docker containers managed by Gitea. Enable in Dashboard → Admin Panel → Actions.
User and Organization Management
Create organizations for team projects:
# Via web interface: Dashboard → Create OrganizationOrganizations provide:
- Team management
- Repository access control
- Shared settings
- Project grouping
Create teams within organizations and assign users.
Backup
Backup Gitea data and database:
docker compose exec gitea /app/gitea/gitea dump -c /data/gitea/conf/app.ini
docker compose cp gitea:/data/gitea-dump-*.zip ./Backup includes:
- Repository data
- Database
- Attachments
- Avatars
- Configuration
Updates
Update Forgejo/Gitea:
cd /etc/docker/compose/gitea
docker compose pull
docker compose up -dVerify the update:
docker compose logs -f giteaUseful Commands
List all users:
docker compose exec gitea /app/gitea/gitea admin user list -c /data/gitea/conf/app.iniCreate admin user:
docker compose exec gitea /app/gitea/gitea admin user create --username admin --password password --email admin@example.com -c /data/gitea/conf/app.iniFor small teams and personal projects, Gitea provides all the features of GitHub: repositories, pull requests, issue tracking, wiki, code review, and CI/CD. Zero cloud dependency, complete data ownership, and minimal resource requirements.
Troubleshooting
If SSH push fails with "connection refused", ensure:
docker compose logs gitea | grep -i ssh
docker compose ps | grep giteaPort 222 must be exposed and accessible. Verify firewall rules:
ss -tlnp | grep 222For database connection errors, check environment variables are passed correctly:
docker compose config | grep GITEA__database