Gitea
Audited from github.com/go-gitea/gitea
Gitea is a self-hosted, all-in-one software development platform that bundles Git hosting, code review, issue tracking, a package registry and GitHub Actions-compatible CI/CD into a single Go binary.
- Language
- Go
- Database
- SQLite
- Hosting
- Docker
- License
- MIT
- Running for
- 8 years
- Team
- 20+ people
Why this architecture
One Go process fronted by XORM abstracts four relational databases and pluggable search/storage backends, so the same codebase scales from a single SQLite file on a Raspberry Pi to a MySQL- or PostgreSQL-backed team instance.
Tech stack by layer
10 technologies · audited Sep 17, 2026- VueVue 3.5 components inside the server-rendered templates, wired up via @vitejs/plugin-vue.
- ViteBundles web_src/js and web_src/css; referenced as a frontend config in the Makefile.
- Tailwind CSSUtility CSS (tailwind.config.ts) alongside the legacy Less stylesheets.
- CodeMirrorIn-browser code, Markdown and JSON editors (@codemirror/lang-markdown, -json, autocomplete, lint).
- Chart.jsRepository activity and contribution graphs via chart.js and chartjs-plugin-zoom.
- GoThe server, CLI (cmd/) and background workers are one Go module, gitea.dev, go 1.27.
- XORMORM for every supported database, used throughout models/ (xorm.io/xorm, xorm.io/builder).
- Gitea ActionsBuilt-in CI runner compatible with GitHub Actions workflow syntax.
- ChiHTTP routing for the web and API routers (go-chi/chi/v5, go-chi/cors).
- SQLiteDefault single-file database for small instances (mattn/go-sqlite3 and modernc.org/sqlite).
- MySQLSupported production database via go-sql-driver/mysql; covered by its own integration-test matrix.
- PostgreSQLSupported production database via lib/pq.
- RedisCache, queue and session backend (redis/go-redis/v9, modules/cache, modules/queue, modules/nosql).
- BleveEmbedded full-text indexer for code and issue search, the default when no external engine is set (blevesearch/bleve/v2).
- MinIOS3-compatible object storage backend for LFS objects, avatars and attachments (minio-go/v7).
- DockerThree-stage build (frontend, backend, alpine runtime); s6 supervises sshd and the web process.
- GitHub ActionsGitea's own CI: DB test matrix, Playwright e2e, Docker dry-run, nightly Snapcraft and release workflows.
- Nixflake.nix provides a reproducible Nix build/dev shell.
- PlaywrightBrowser end-to-end tests (@playwright/test, tests/e2e, playwright.config.ts).
- ESLintTypeScript/Vue linting via eslint.config.ts with @stylistic/eslint-plugin.
- StylelintCSS/Less linting via @stylistic/stylelint-plugin.
Gitea architecture diagram
Open SVGDiagram as text
- Web UI (templates · Vue) → Gitea server (Go · Chi): HTTP
- Git clients (HTTPS · SSH) → Gitea server (Go · Chi): smart HTTP
- Git clients (HTTPS · SSH) → SSH server (sshd (s6)): git over SSH
- Actions runners (Gitea Actions) → Gitea server (Go · Chi): Actions tasks
- Gitea server (Go · Chi) → Database (SQLite / MySQL / Postgres): XORM
- Gitea server (Go · Chi) → Redis (cache · queue · session): cache
- Gitea server (Go · Chi) → Queues & indexers (modules/queue): enqueue
- Queues & indexers (modules/queue) → Code search (Bleve): index
- Gitea server (Go · Chi) → Object storage (MinIO / local): LFS · attachments
Key architectural decisions
6 decisions- 01
One ORM abstracts four relational databases
go.mod pulls in drivers for SQLite (mattn/go-sqlite3, modernc.org/sqlite), MySQL (go-sql-driver/mysql), PostgreSQL (lib/pq) and MSSQL (microsoft/go-mssqldb), all accessed through xorm.io/xorm in models/db. tests/mysql.ini.tmpl, tests/pgsql.ini.tmpl, tests/mssql.ini.tmpl and tests/sqlite.ini.tmpl back a dedicated pull-db-tests.yml CI matrix so no backend regresses silently.
- 02
CI/CD is reimplemented in-repo as Gitea Actions
modules/actions, services/actions and routers/api handle a GitHub Actions-compatible runner and workflow format, so a self-hosted instance gets CI/CD without wiring up Jenkins, Drone or an external runner — the tradeoff is Gitea now maintains its own workflow-syntax compatibility layer.
- 03
Search and object storage are both pluggable
modules/indexer and services/indexer default to an embedded Bleve index (blevesearch/bleve/v2) but can delegate to Meilisearch (meilisearch/meilisearch-go) for larger instances; modules/storage does the same for file storage, defaulting to local disk but supporting MinIO/S3 (minio-go/v7) for LFS objects, avatars and attachments.
- 04
Schema migrations are versioned by release, not squashed
modelmigration/v1_6 through v1_27 (plus v28) keep every historical schema-migration step as its own package rather than collapsing history, so an instance can upgrade from a very old release without a separate migration tool.
- 05
The container runs an init system, not a single process
The final Docker stage installs openssh and s6, and its CMD is s6-svscan rather than the gitea binary directly, because the container must run both the SSH server for git+ssh access and the web/API server as supervised sibling processes.
- 06
Frontend build tooling migrated to Vite while legacy jQuery remains
package.json still lists jquery (4.0.0) and Less alongside Vue 3, Vite and Tailwind CSS; the Makefile FRONTEND_SOURCES variable finds files under both web_src/js and web_src/css, reflecting an incremental migration rather than a rewrite.
How Gitea is built
How Gitea is structured
Gitea is one Go module (gitea.dev, go.mod) with the frontend build tooling layered on top via a root package.json (pnpm workspace, packageManager: pnpm@12.4.0). The Go side follows a layered architecture: models holds XORM-mapped structs and queries organized by domain (models/repo, models/issues, models/actions, models/packages, models/webhook, and 20-odd others), services holds business logic that composes those models (services/pull, services/mirror, services/actions, services/lfs), modules holds lower-level, mostly dependency-free utilities (modules/git, modules/storage, modules/queue, modules/markup), and routers wires HTTP requests in routers/web, routers/api and routers/private to those services. modelmigration keeps every schema change since v1.6 as its own versioned package. The frontend lives under web_src/js and web_src/css (visible via the Makefile's FRONTEND_SOURCES), built by Vite and rendered into Go html/template files in templates/.
Frontend
package.json shows a frontend that is mid-migration rather than a clean modern stack: Vue 3.5 (@vitejs/plugin-vue) and Tailwind CSS 3.4 sit alongside jQuery 4.0.0 and Less, all bundled by Vite 8. The CodeMirror 6 family (@codemirror/lang-markdown, -json, autocomplete, lint, plus community language packages for Nix, Svelte and Elixir) powers in-browser code and Markdown editing; Mermaid and KaTeX render diagrams and math in rendered Markdown; Chart.js with chartjs-plugin-zoom draws contribution and activity graphs; swagger-ui-dist serves the generated API docs. stylelint.config.ts and eslint.config.ts (with @stylistic plugins) lint the CSS and TypeScript/Vue code respectively.
Backend & APIs
The HTTP layer uses go-chi/chi/v5 for routing and go-chi/cors for CORS, exposed through routers/api (the REST API also generates an OpenAPI/Swagger spec via build/openapi3gen) and routers/web. Authentication supports LDAP (go-ldap/ldap/v3), WebAuthn/passkeys (go-webauthn/webauthn), TOTP (pquerna/otp), JWT (golang-jwt/jwt/v5) and OAuth2/OIDC providers via markbates/goth; caddyserver/certmagic provides automatic TLS certificate management. modules/actions and services/actions implement Gitea Actions, a CI runner that consumes GitHub Actions-style workflow YAML so pipelines run without an external CI product. Rendering support includes alecthomas/chroma for syntax highlighting and PuerkitoBio/goquery for HTML processing in the migration importers under services/migrations, which can pull repositories from GitHub, GitLab and Gitea.com.
Data & persistence
xorm.io/xorm is the single ORM used across models/, mapped onto whichever of four drivers is configured: SQLite (mattn/go-sqlite3 or the CGO-free modernc.org/sqlite), MySQL (go-sql-driver/mysql), PostgreSQL (lib/pq) or MSSQL (microsoft/go-mssqldb). tests/mysql.ini.tmpl, tests/pgsql.ini.tmpl, tests/mssql.ini.tmpl and tests/sqlite.ini.tmpl back a per-database CI matrix (pull-db-tests.yml). Caching, queueing and distributed locking go through Redis (redis/go-redis/v9, modules/cache, modules/queue, modules/nosql), and full-text search runs on an embedded Bleve index by default (blevesearch/bleve/v2) with meilisearch/meilisearch-go as an external alternative. File storage (modules/storage) defaults to local disk but also supports S3-compatible object storage through minio-go/v7 for LFS objects, avatars and attachments.
Build, test & deploy
Makefile orchestrates both halves: make frontend runs the pnpm/Vite build, make backend compiles the Go binary with bindata/timetzdata tags, and make test-backend/make test-frontend run the respective suites. Go linting runs through golangci-lint v2; .golangci.yml configures it. Frontend linting runs ESLint and Stylelint, and tests/e2e plus pull-e2e-tests.yml run Playwright against a built instance. CI (.github/workflows) also covers Docker dry-run builds, license compliance (cron-licenses.yml, assets/go-licenses.json), translation sync via Crowdin, and nightly Snapcraft and release-tag workflows.
Self-hosting notes
The production Dockerfile is a three-stage build: a Node+pnpm stage compiles the frontend (make frontend), a Go stage compiles the backend (make backend), and a final alpine image installs openssh, git and s6, running as an unprivileged git user with s6-svscan as the entrypoint's target — necessary because the container serves both git+ssh (via the bundled SSH server) and HTTP simultaneously. Dockerfile.rootless provides a rootless variant. Beyond Docker, snap/snapcraft.yaml packages Gitea for Snap and flake.nix provides a Nix build; contrib/ includes an upgrade.sh script and a Grafana monitoring mixin (contrib/grafana-monitoring-mixin) for operators who already run Prometheus and Grafana.
What to copy (and what not to)
The versioned-migration package (modelmigration/v1_6 … v1_27) is a strong pattern for any long-lived self-hosted product where operators skip releases: every schema step stays runnable in order instead of being squashed into an unreviewable diff. The pluggable-backend pattern for search (Bleve/Meilisearch) and storage (disk/MinIO) is worth copying when a tool needs to scale from a hobby deployment to a larger one without a rewrite. Be more cautious about copying the frontend as-is: mixing jQuery, Less and Vue in the same bundle is a byproduct of a long migration, not a deliberate architecture, and a new project has no reason to start there.
Sources & repo audit
- go.mod (XORM, Chi, Bleve, Meilisearch, Redis, MinIO, 4 DB drivers)
- package.json (Vue, Vite, CodeMirror, Tailwind CSS, Playwright)
- Dockerfile (three-stage build, s6 runtime)
- Makefile (web_src build inputs, golangci-lint, Playwright/Stylelint targets)
- tests/ (per-database integration-test config templates)
Independent analysis of repository at github.com/go-gitea/gitea. Spotted an inaccuracy? Use the claim form to request a correction.
Show this stack on your README or website
[](https://stackitfast.com/project/gitea)Paste it into your README. It shows your current stack and links to this page.
Scaffold it with your agent
Paste this prompt into Claude Code, Cursor, Windsurf or AGY to start a project with Gitea's architecture.
- 1Copy the promptThe full markdown spec, with every layer and decision.
- 2Open your AI toolClaude Code, Cursor, Windsurf or Copilot, in a new repo.
- 3Paste and scaffoldUse it as the first instruction; review before you ship.
# MISSION: Scaffold "Gitea" Production Architecture
You are an expert Senior Staff Software Architect and Full-Stack Engineer. Your mission is to scaffold and implement a production-grade, highly reliable, and modular codebase following the proven architecture of **Gitea**.
---
## 1. PROJECT SPECIFICATIONS & BENCHMARK
- **Reference Architecture**: Gitea
- **What It Does**: Gitea is a self-hosted, all-in-one software development platform that bundles Git hosting, code review, issue tracking, a package registry and GitHub Actions-compatible CI/CD into a single Go binary.
- **Domain & Category**: Lightweight Self-Hosted Git Service
- **Production Scale**: 20+ people
- **Development Mode**: CLASSIC
- **Architectural Rationale**: One Go process fronted by XORM abstracts four relational databases and pluggable search/storage backends, so the same codebase scales from a single SQLite file on a Raspberry Pi to a MySQL- or PostgreSQL-backed team instance.
- **Live Website Reference**: https://about.gitea.com
- **Source Repository**: https://github.com/go-gitea/gitea
---
## 2. PRODUCTION TECH STACK
- **Full Stack Array**: Go, TypeScript, Vue, XORM, SQLite, PostgreSQL, MySQL, Redis, Bleve, Docker
- **Primary Language(s)**: Go, TypeScript
- **License of the reference repo**: MIT
- **Frontend**: Vue — Vue 3.5 components inside the server-rendered templates, wired up via @vitejs/plugin-vue.; Vite — Bundles web_src/js and web_src/css; referenced as a frontend config in the Makefile.; Tailwind CSS — Utility CSS (tailwind.config.ts) alongside the legacy Less stylesheets.; CodeMirror — In-browser code, Markdown and JSON editors (@codemirror/lang-markdown, -json, autocomplete, lint).; Chart.js — Repository activity and contribution graphs via chart.js and chartjs-plugin-zoom.
- **Backend & APIs**: Go — The server, CLI (cmd/) and background workers are one Go module, gitea.dev, go 1.27.; XORM — ORM for every supported database, used throughout models/ (xorm.io/xorm, xorm.io/builder).; Gitea Actions — Built-in CI runner compatible with GitHub Actions workflow syntax.; Chi — HTTP routing for the web and API routers (go-chi/chi/v5, go-chi/cors).
- **Data & persistence**: SQLite — Default single-file database for small instances (mattn/go-sqlite3 and modernc.org/sqlite).; MySQL — Supported production database via go-sql-driver/mysql; covered by its own integration-test matrix.; PostgreSQL — Supported production database via lib/pq.; Redis — Cache, queue and session backend (redis/go-redis/v9, modules/cache, modules/queue, modules/nosql).; Bleve — Embedded full-text indexer for code and issue search, the default when no external engine is set (blevesearch/bleve/v2).; MinIO — S3-compatible object storage backend for LFS objects, avatars and attachments (minio-go/v7).
- **Infrastructure & deploy**: Docker — Three-stage build (frontend, backend, alpine runtime); s6 supervises sshd and the web process.; GitHub Actions — Gitea's own CI: DB test matrix, Playwright e2e, Docker dry-run, nightly Snapcraft and release workflows.; Nix — flake.nix provides a reproducible Nix build/dev shell.
- **Tooling, testing & ops**: Playwright — Browser end-to-end tests (@playwright/test, tests/e2e, playwright.config.ts).; ESLint — TypeScript/Vue linting via eslint.config.ts with @stylistic/eslint-plugin.; Stylelint — CSS/Less linting via @stylistic/stylelint-plugin.
---
## 3. KEY ARCHITECTURAL DECISIONS (audited from https://github.com/go-gitea/gitea @ 4892a55)
1. **One ORM abstracts four relational databases**: go.mod pulls in drivers for SQLite (mattn/go-sqlite3, modernc.org/sqlite), MySQL (go-sql-driver/mysql), PostgreSQL (lib/pq) and MSSQL (microsoft/go-mssqldb), all accessed through xorm.io/xorm in models/db. tests/mysql.ini.tmpl, tests/pgsql.ini.tmpl, tests/mssql.ini.tmpl and tests/sqlite.ini.tmpl back a dedicated pull-db-tests.yml CI matrix so no backend regresses silently.
2. **CI/CD is reimplemented in-repo as Gitea Actions**: modules/actions, services/actions and routers/api handle a GitHub Actions-compatible runner and workflow format, so a self-hosted instance gets CI/CD without wiring up Jenkins, Drone or an external runner — the tradeoff is Gitea now maintains its own workflow-syntax compatibility layer.
3. **Search and object storage are both pluggable**: modules/indexer and services/indexer default to an embedded Bleve index (blevesearch/bleve/v2) but can delegate to Meilisearch (meilisearch/meilisearch-go) for larger instances; modules/storage does the same for file storage, defaulting to local disk but supporting MinIO/S3 (minio-go/v7) for LFS objects, avatars and attachments.
4. **Schema migrations are versioned by release, not squashed**: modelmigration/v1_6 through v1_27 (plus v28) keep every historical schema-migration step as its own package rather than collapsing history, so an instance can upgrade from a very old release without a separate migration tool.
5. **The container runs an init system, not a single process**: The final Docker stage installs openssh and s6, and its CMD is s6-svscan rather than the gitea binary directly, because the container must run both the SSH server for git+ssh access and the web/API server as supervised sibling processes.
6. **Frontend build tooling migrated to Vite while legacy jQuery remains**: package.json still lists jquery (4.0.0) and Less alongside Vue 3, Vite and Tailwind CSS; the Makefile FRONTEND_SOURCES variable finds files under both web_src/js and web_src/css, reflecting an incremental migration rather than a rewrite.
---
## 4. NON-NEGOTIABLE ARCHITECTURAL GUARDRAILS
1. **Monorepo & Modular Separation**:
- Structure as a Turborepo monorepo with strict package boundaries:
- `apps/web`: Application UI, routing, layouts, and server endpoints.
- `packages/ui`: Shared design tokens, CSS variables, and Radix UI primitive components.
- `packages/db`: Database schemas, client singleton, declarative migrations, and seed scripts.
- `packages/config`: Shared TypeScript, ESLint, and build configurations.
2. **Strict Type Safety & Zero `any` Policy**:
- Enable `strict: true`, `noImplicitAny: true`, and `strictNullChecks: true`.
- Validate ALL external inputs, API request bodies, and query parameters with **Zod** schemas before execution.
3. **Frontend & Rendering Guidelines**:
- Isolate interactive UI state to leaf components. Keep core pages lightweight and performant.
4. **Design System & Aesthetics**:
- Keep every color, radius, shadow and font in a single token file (CSS variables) and consume tokens everywhere; never hardcode hex values in components.
- Prefer crisp 1px borders and one subtle shadow scale over blurry default shadows. Pair one sans-serif for body/headings with one monospace for tags, badges, metrics, and code.
5. **Data Layer & Reliability**:
- Write declarative schema definitions with foreign keys, composite indexes on queried filters, and automated timestamp triggers.
- Use connection pooling and prepared statements for serverless database execution.
---
## 5. STEP-BY-STEP SCAFFOLDING ROADMAP
- **Phase 1: Workspace & Root Config**: Initialize package manager, monorepo configuration (`turbo.json`, `tsconfig.base.json`, `package.json`).
- **Phase 2: Database Schema & Client**: Set up the data layer (SQLite, MySQL, PostgreSQL, Redis, Bleve, MinIO): client, connection pool, models, and migration scripts.
- **Phase 3: Design Tokens & UI Primitives**: Build accessible `Button`, `Input`, `Card`, `Badge`, and layout wrappers inside `packages/ui`.
- **Phase 4: Core Application Routes & Handlers**: Implement primary authentication, user session handling, and application routes.
- **Phase 5: Quality Assurance & Build Verification**: Run `tsc --noEmit`, ESLint, Prettier, and smoke test suites to ensure zero compilation or runtime errors.
---
## 6. EXECUTION INSTRUCTIONS
1. Review all specifications, architectural guardrails, and stack choices above.
2. Present the full monorepo directory tree structure.
3. Systematically generate the complete, production-ready codebase according to the 5-phase roadmap above — starting with the root workspace setup, followed by the database schema, UI design system package, and full-stack application routes until the repository is fully scaffolded and ready to run.Frequently asked about Gitea
What is Gitea built with?
Gitea is a Go backend (Chi router, XORM ORM) with a server-rendered frontend that mixes Vue 3 components, CodeMirror editors and Tailwind CSS, bundled with Vite. It supports four relational databases and ships as a single Go binary.
Which databases does Gitea support?
SQLite (default, via mattn/go-sqlite3 or modernc.org/sqlite), MySQL, PostgreSQL and MSSQL, all through the same XORM data layer. Each backend has its own integration-test configuration under tests/.
Does Gitea include CI/CD?
Yes. Gitea Actions (modules/actions, services/actions) is a built-in runner compatible with GitHub Actions workflow syntax, so pipelines can run without a separate CI product.
Can I self-host Gitea?
Yes, self-hosting is the primary use case. The Dockerfile produces a container (a rootless variant also exists as Dockerfile.rootless) supervised by s6, and there are Snapcraft (snap/) and Nix (flake.nix) packaging paths as well.
Does Gitea need external search or object storage?
No, both are optional. Search defaults to an embedded Bleve index with Meilisearch available as an external option; file storage (LFS objects, avatars, attachments) defaults to local disk with MinIO/S3-compatible storage as an alternative.
One email a month: new deep dives and stack trends
New source-audited architectures, head-to-head comparisons and the monthly stack report. No spam, unsubscribe anytime.
Similar architectures
- GotifyClassicSelf-Hosted Real-Time Push Server · 2-5 PeopleShares Go · TypeScript · SQLite
- PrismaClassicNext-Generation Node.js & TypeScript ORM · 1M+ MAUShares TypeScript · PostgreSQL · MySQL
- AutheliaClassicAuthentication & SSO Proxy Companion · 6-20 PeopleShares Go · TypeScript · PostgreSQL
- NocoDBHybridSmart Database Spreadsheet · 6-20 peopleShares TypeScript · Vue · PostgreSQL
- HoppscotchClassicAPI Testing Client · 6-20 peopleShares Vue · TypeScript · PostgreSQL
- NextcloudClassicSelf-Hosted Collaborative Productivity Suite · 20+ peopleShares TypeScript · Vue · MySQL