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:

  1. Configuration Loading: setting.InitCfgProvider parses app.ini and establishes global settings.
  2. Router Initialization: web.NewRouter() creates the chi-based router instance.
  3. Route Registration: The router passes to routers/web.Routes() for HTML endpoints and routers/api/v1.Routes() for JSON endpoints.
  4. Middleware Application: web.Use() attaches authentication, CSRF, gzip, and logging middleware to route groups.
  5. Request Handling: UI handlers in routers/web/* and API handlers in routers/api/* call service layers (services/repository, services/notify) which delegate Git operations to modules/git.
  6. 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/web wraps chi routing, while routers/web and routers/api handle UI and API endpoints respectively.
  • Git Operations: modules/git provides 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, and modules/graceful handle configuration, observability, and lifecycle management.
  • Data Contracts: modules/structs defines 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →