Gitea Architecture Explained: Key Modules and System Design
Gitea is a modular Go application that separates HTTP routing (modules/web), Git operations (modules/git), business logic (services/), and interface layers (routers/), with clear boundaries between web UI, JSON API, and core infrastructure.
The open-source Git service Gitea (go-gitea/gitea) follows a domain-driven, modular architecture written in Go. Understanding its key modules helps developers extend functionality, debug issues, or contribute to the core platform. The codebase organizes functionality into distinct packages with specific responsibilities, from HTTP request handling in modules/web to low-level Git binary interactions in modules/git.
Core HTTP Routing and Interface Layer
modules/web - The Router Engine
Located in modules/web/router.go, this package provides a thin wrapper around the chi router. The web.NewRouter() function creates the foundational routing instance used throughout the application, while web.Use() registers global middleware chains for authentication, CSRF protection, CORS, and gzip compression.
The core Router struct and initialization logic occupy lines 42-78 of router.go, establishing the entry point for all HTTP traffic. Route registration uses methods like Get(), Post(), and Group() to organize endpoints.
package main
import (
"net/http"
"code.gitea.io/gitea/modules/web"
)
func main() {
// Create a new router
r := web.NewRouter()
// Register a GET handler at /hello
r.Get("/hello", func(ctx *web.Context) {
ctx.Resp.WriteHeader(http.StatusOK)
ctx.Resp.Write([]byte("👋 Hello from Gitea!"))
})
// Start the HTTP server (normally done by the web server wrapper)
http.ListenAndServe(":3000", r)
}
You can add custom middleware using r.Use() as implemented in the source:
package main
import (
"net/http"
"code.gitea.io/gitea/modules/web"
)
// Simple logger middleware
func requestID(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Request-ID", "abc123")
next.ServeHTTP(w, r)
})
}
func main() {
r := web.NewRouter()
// Apply middleware globally
r.Use(requestID)
r.Get("/", func(ctx *web.Context) {
ctx.Resp.Write([]byte("root"))
})
http.ListenAndServe(":3000", r)
}
routers/web - Web UI Route Handlers
The routers/web package contains all HTML-generating endpoints. The routers/web/web.go file defines Routes() around lines 59-115, which registers every user-facing page including repository views, issue trackers, and admin panels. These handlers render templates and utilize the services layer for business operations.
routers/api - JSON API Endpoints
REST API routes live in routers/api/v1/api.go, with the entry point Routes() around lines 872-904. These handlers return JSON structures defined in modules/structs, maintaining strict separation between internal database models and public API contracts.
Git Operations and Repository Management
modules/git - Git Plumbing
This module in modules/git/git.go centralizes all interaction with the underlying git binary. Originally an external dependency (code.gitea/git), it now lives in-tree and handles repository initialization, fetch/push operations, submodule management, and low-level Git object manipulation. Both UI and API layers delegate Git-specific tasks here rather than calling the binary directly.
services/repository - High-Level Repository Actions
Business logic for repository lifecycle operations resides in services/repository/, including fork.go for forking, transfer, creation, and migration logic. The repo_service.Fork() function (lines 1-30) provides a concrete example of how services orchestrate database updates, Git operations, and permission checks.
package main
import (
"fmt"
repo_service "code.gitea.io/gitea/services/repository"
repo_model "code.gitea.io/gitea/models/repo"
)
func forkDemo(original *repo_model.Repository, userID int64) {
// repo_service.Fork creates a new fork for the given user
fork, err := repo_service.Fork(original, userID)
if err != nil {
fmt.Printf("fork failed: %v\n", err)
return
}
fmt.Printf("new fork created at %s\n", fork.HTMLURL())
}
Configuration, Logging, and Infrastructure
modules/setting - Global Configuration
The modules/setting/setting.go file loads app.ini configuration at startup via setting.LoadCommonSettings() and stores runtime values including AppVer, RunMode, service options, and OAuth2 settings. All other packages import this module to access configuration, ensuring centralized management of deployment parameters.
modules/log - Structured Logging
Defined in modules/log/log.go, this module initializes loggers that write to console, file, or syslog. The logger initializes in main.init() and flushes on shutdown. Every package uses log.Info() and log.Error() for consistent, structured output across the application.
modules/graceful - Server Lifecycle
Located in modules/graceful/server.go, this module manages graceful shutdown and restart procedures. It ensures in-flight HTTP requests complete before the process exits, preventing data corruption during deployments.
modules/util - Common Utilities
The modules/util/util.go package provides shared helper functions for string manipulation, file path operations, and time conversions. This keeps domain-specific modules focused on their primary responsibilities without utility clutter.
modules/structs - API Data Models
Public data structures for API responses live in modules/structs/ (e.g., user.go). These structs define JSON schemas for entities like User, Repository, and Issue, decoupling internal database models from external API contracts.
Authentication and Notification Services
services/auth - Authentication Framework
The services/auth/ directory implements pluggable authentication methods through the Method interface. Files like oauth2.go (lines 5-26) support OAuth2, HTTP basic auth, reverse-proxy authentication, and session-based login. Router middleware uses this framework to protect routes and identify users according to the source code in go-gitea/gitea.
services/notify - Event Distribution
When repository events occur (new pull requests, issue comments), services/notify/notify.go triggers email delivery, webhooks, and internal notifications. This service acts as the event bus for asynchronous side effects, keeping handlers focused on request processing.
Application Startup and Request Flow
The Gitea architecture follows a clear initialization sequence defined in main.go:
- Configuration Loading:
setting.InitCfgProviderparsesapp.iniand establishes global settings. - Router Initialization:
web.NewRouter()creates the chi-based router instance. - Route Registration: The router passes to
routers/web.Routes()for HTML endpoints androuters/api/v1.Routes()for JSON endpoints. - Middleware Application:
web.Use()attaches authentication, CSRF, gzip, and logging middleware to route groups. - Request Handling: UI handlers in
routers/web/*and API handlers inrouters/api/*call service layers (services/repository,services/notify) which delegate Git operations tomodules/git. - Persistence: Database models in
models/*wrap XORM structs, though higher-level code prefers service layers to isolate database specifics from HTTP handlers.
Summary
- HTTP Layer:
modules/webwraps chi routing, whilerouters/webandrouters/apihandle UI and API endpoints respectively. - Git Operations:
modules/gitprovides the sole interface to Git binaries, used by service layers for repository management. - Business Logic:
services/packages (auth, repository, notify) contain domain logic consumed by both UI and API. - Infrastructure:
modules/setting,modules/log, andmodules/gracefulhandle configuration, observability, and lifecycle management. - Data Contracts:
modules/structsdefines public API schemas, maintaining clean separation between internal models and external interfaces.
Frequently Asked Questions
What router does Gitea use under the hood?
Gitea uses the chi router, wrapped by the internal modules/web package. The web.NewRouter() function in modules/web/router.go creates the router instance, and web.Use() registers middleware like authentication and CSRF protection.
Where is the main entry point for Gitea's HTTP server?
The primary HTTP routing setup occurs in routers/web/web.go for the UI and routers/api/v1/api.go for the API. Both are initialized from main.go, which loads configuration via setting.InitCfgProvider before starting the server.
How does Gitea handle Git operations without libgit2?
Gitea calls the native git binary directly through the abstraction layer in modules/git/git.go. This module handles all repository initialization, cloning, pushing, and submodule operations as external process calls rather than using libgit2 bindings.
What is the difference between services and modules in Gitea?
Modules (modules/) provide low-level infrastructure utilities (routing, logging, Git binary interaction, configuration). Services (services/) contain high-level business logic like repository forking (services/repository/fork.go) and authentication (services/auth/) that coordinate multiple modules to fulfill user actions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →