What Is Gitea and Why Run It on Windows Server 2022
Gitea is a lightweight, self-hosted Git service written in Go. It provides a GitHub-style web interface for browsing repositories, reviewing pull requests, managing organizations, and running basic CI/CD through Gitea Actions. Because it ships as a single binary with no runtime dependencies, it is easy to deploy on Windows Server 2022 without installing Node.js, Ruby, or Java. Teams that require an on-premises Git server for compliance, air-gapped environments, or cost reasons commonly choose Gitea for its low resource footprint and simple administration.
Downloading Gitea for Windows
Gitea provides pre-built Windows binaries on its releases page. Download the latest 64-bit executable and place it in a dedicated service directory:
# Create the Gitea directory structure
New-Item -ItemType Directory -Path "C:Giteabin" -Force | Out-Null
New-Item -ItemType Directory -Path "C:Giteadata" -Force | Out-Null
New-Item -ItemType Directory -Path "C:Gitealog" -Force | Out-Null
New-Item -ItemType Directory -Path "C:Gitearepos" -Force | Out-Null
# Download the binary (check https://dl.gitea.com/gitea/ for the latest version)
$giteaVersion = "1.22.1"
$giteaUrl = "https://dl.gitea.com/gitea/$giteaVersion/gitea-$giteaVersion-windows-4.0-amd64.exe"
Invoke-WebRequest -Uri $giteaUrl -OutFile "C:Giteabingitea.exe"
Verify the download by checking the SHA256 checksum published on the Gitea releases page:
Get-FileHash -Algorithm SHA256 "C:Giteabingitea.exe" | Select-Object Hash
Initial Configuration: app.ini
Gitea reads its configuration from a file named app.ini, located at C:Giteadatacustomconfapp.ini by default. Create this file before the first run to pre-configure the most important settings:
New-Item -ItemType Directory -Path "C:Giteadatacustomconf" -Force | Out-Null
$appIni = @"
APP_NAME = My Company Gitea
RUN_MODE = prod
RUN_USER = git
[server]
DOMAIN = gitea.example.com
HTTP_PORT = 3000
ROOT_URL = http://gitea.example.com/
LOCAL_ROOT_URL = http://localhost:3000/
SSH_DOMAIN = gitea.example.com
SSH_PORT = 22
SSH_LISTEN_PORT = 2222
START_SSH_SERVER = true
OFFLINE_MODE = false
[database]
DB_TYPE = sqlite3
PATH = C:/Gitea/data/gitea.db
[repository]
ROOT = C:/Gitea/repos
[log]
ROOT_PATH = C:/Gitea/log
MODE = file
LEVEL = Info
[security]
INSTALL_LOCK = false
SECRET_KEY = CHANGEME_replace_with_random_64char_string
INTERNAL_TOKEN = CHANGEME_replace_with_random_token
PASSWORD_HASH_ALGO = argon2
[service]
DISABLE_REGISTRATION = false
REQUIRE_SIGNIN_VIEW = false
ENABLE_NOTIFY_MAIL = false
ALLOW_ONLY_INTERNAL_REGISTRATION = true
[mailer]
ENABLED = false
"@
Set-Content -Path "C:Giteadatacustomconfapp.ini" -Value $appIni -Encoding UTF8
Replace the SECRET_KEY and INTERNAL_TOKEN values with random strings. You can generate them with:
-join ((1..64) | ForEach-Object { '{0:X}' -f (Get-Random -Maximum 16) })
Running Gitea as a Windows Service with NSSM
NSSM (Non-Sucking Service Manager) wraps any executable as a Windows service, handling restarts, logging, and startup order. Install NSSM first:
winget install --id NSSM.NSSM -e --source winget
Or download it manually from https://nssm.cc/download and place nssm.exe in a folder on the PATH. Then register Gitea as a service:
nssm install gitea "C:Giteabingitea.exe"
nssm set gitea AppParameters "web --config C:Giteadatacustomconfapp.ini"
nssm set gitea AppDirectory "C:Giteabin"
nssm set gitea DisplayName "Gitea Git Service"
nssm set gitea Description "Self-hosted Git service"
nssm set gitea Start SERVICE_AUTO_START
nssm set gitea AppStdout "C:Gitealogservice_stdout.log"
nssm set gitea AppStderr "C:Gitealogservice_stderr.log"
nssm set gitea AppRotateFiles 1
nssm set gitea AppRotateOnline 1
nssm set gitea AppRotateSeconds 86400
# Start the service
nssm start gitea
Verify the service is running:
Get-Service -Name gitea
# Status: Running
If you prefer not to use NSSM, Gitea also supports native Windows service registration:
cd C:Giteabin
.gitea.exe service install --config C:Giteadatacustomconfapp.ini
.gitea.exe service start
Database Setup: SQLite, MySQL, and PostgreSQL
The app.ini above uses SQLite, which is fine for teams up to around 20-50 users. For larger installations or better concurrency, use MySQL or PostgreSQL.
For MySQL, first create the database and user:
-- Run in MySQL client
CREATE DATABASE gitea CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'gitea'@'localhost' IDENTIFIED BY 'StrongPassword123!';
GRANT ALL PRIVILEGES ON gitea.* TO 'gitea'@'localhost';
FLUSH PRIVILEGES;
Then update the [database] section in app.ini:
[database]
DB_TYPE = mysql
HOST = 127.0.0.1:3306
NAME = gitea
USER = gitea
PASSWD = StrongPassword123!
CHARSET = utf8mb4
For PostgreSQL:
-- Run as postgres superuser
CREATE ROLE gitea WITH LOGIN PASSWORD 'StrongPassword123!';
CREATE DATABASE gitea OWNER gitea;
GRANT ALL PRIVILEGES ON DATABASE gitea TO gitea;
[database]
DB_TYPE = postgres
HOST = 127.0.0.1:5432
NAME = gitea
USER = gitea
PASSWD = StrongPassword123!
SCHEMA =
SSL_MODE = disable
Running the Setup Wizard
With the service running and the firewall port open, navigate to http://SERVER_IP:3000 in a browser. The Gitea installation wizard appears on the first visit. If you pre-configured app.ini, most fields are already populated. The wizard will:
- Confirm the database connection and create the schema
- Set the administrator account credentials
- Finalize the
INSTALL_LOCK = trueflag in app.ini to prevent re-running the wizard
Open the firewall port before trying to access the interface:
New-NetFirewallRule -DisplayName "Gitea HTTP" -Direction Inbound `
-Protocol TCP -LocalPort 3000 -Action Allow
New-NetFirewallRule -DisplayName "Gitea SSH" -Direction Inbound `
-Protocol TCP -LocalPort 2222 -Action Allow
Configuring HTTPS with a Certificate
To enable HTTPS directly in Gitea (without a reverse proxy), place your certificate and private key files on the server and update app.ini:
[server]
PROTOCOL = https
ROOT_URL = https://gitea.example.com/
HTTP_PORT = 443
CERT_FILE = C:/Gitea/ssl/cert.pem
KEY_FILE = C:/Gitea/ssl/key.pem
Restart the service after modifying app.ini:
nssm restart gitea
IIS as a Reverse Proxy for Gitea
Running IIS as a reverse proxy in front of Gitea lets you host Gitea on port 3000 internally while serving it over port 443 with an IIS-managed certificate. Install the URL Rewrite and Application Request Routing (ARR) modules, then configure a site:
Import-Module WebAdministration
# Enable proxy in ARR
Set-WebConfigurationProperty -PSPath "MACHINE/WEBROOT/APPHOST" `
-Filter "system.webServer/proxy" -Name "enabled" -Value $true
# Create the reverse proxy site
New-WebSite -Name "Gitea" -Port 443 -PhysicalPath "C:inetpubgitea" `
-Ssl -SslFlags 0
# web.config with URL Rewrite rule for the Gitea site
$webConfig = @"
"@
New-Item -Path "C:inetpubgitea" -ItemType Directory -Force | Out-Null
Set-Content -Path "C:inetpubgiteaweb.config" -Value $webConfig
Update ROOT_URL in app.ini to the HTTPS address, and set HTTP_PORT = 3000 with PROTOCOL = http (Gitea itself stays on HTTP; IIS handles TLS).
Webhooks and CI Integration with Gitea Actions
Gitea supports webhooks at the repository, organization, and system level. To configure a webhook that notifies a build server on every push, go to the repository settings in the web UI, or configure it via the Gitea API:
# Gitea REST API — create a push webhook on a repo
$headers = @{
"Authorization" = "token YOUR_GITEA_ACCESS_TOKEN"
"Content-Type" = "application/json"
}
$body = @{
type = "gitea"
config = @{ url = "https://jenkins.example.com/gitea-webhook/post"; content_type = "json" }
events = @("push", "pull_request")
active = $true
} | ConvertTo-Json
Invoke-RestMethod -Uri "http://gitea.example.com/api/v1/repos/myorg/myrepo/hooks" `
-Method Post -Headers $headers -Body $body
Gitea Actions (available in Gitea 1.19+) uses workflow YAML files in .gitea/workflows/, compatible with GitHub Actions syntax. A basic CI workflow:
# .gitea/workflows/build.yml
name: Build and Test
on: [push, pull_request]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '8.0.x'
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build
To use Gitea Actions, you must register at least one Gitea Actions Runner. Download the runner binary from the Gitea releases page and register it:
act_runner.exe register --instance http://gitea.example.com `
--token RUNNER_TOKEN_FROM_GITEA_SETTINGS `
--name "WinServer2022-Runner" --no-interactive
act_runner.exe daemon
Backing Up Gitea
Gitea includes a built-in backup command that creates a zip archive of the database, configuration, repositories, and attachments:
# Run as the user that owns Gitea data
C:Giteabingitea.exe dump --config C:Giteadatacustomconfapp.ini `
--file "C:Backupsgitea-backup-$(Get-Date -Format 'yyyyMMdd').zip"
Schedule this as a Windows Scheduled Task to run nightly:
$action = New-ScheduledTaskAction -Execute "C:Giteabingitea.exe" `
-Argument "dump --config C:Giteadatacustomconfapp.ini --file C:Backupsgitea-backup.zip"
$trigger = New-ScheduledTaskTrigger -Daily -At "02:00AM"
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable
Register-ScheduledTask -TaskName "Gitea Backup" -Action $action `
-Trigger $trigger -Settings $settings -RunLevel Highest
Store backups on a separate volume or UNC share, and periodically test restoration by unpacking the archive and running Gitea against it in a staging environment to confirm the backup is valid.