Where Are Gitea Settings Managed? The Complete Three-Layer Configuration Guide

Gitea settings are managed across three complementary layers: a static INI configuration file (app.ini), environment variable overrides, and a dynamic system_setting database table, all coordinated through the centralized modules/setting package.

Understanding where Gitea stores its configuration is essential for administrators and developers working with the go-gitea/gitea codebase. This article breaks down the architecture that handles everything from static server paths to runtime-editable maintenance modes, referencing the actual source implementation.

The Three Configuration Layers in Gitea

Gitea's configuration system operates as a hierarchy that blends file-based defaults with live database storage.

Static INI File (app.ini)

The foundation of Gitea settings management is the app.ini file (or a custom file passed via --config). This static file stores core server options including run mode, domain paths, SSH configuration, and UI defaults.

In modules/setting/setting.go, the InitCfgProvider function creates a ConfigProvider backed by this INI file. The provider parses sections like [server], [database], and [security] into package-level variables such as setting.RunMode, setting.Domain, and setting.SSH.Port. These values are loaded once at startup and remain static unless the file is modified and the service restarted.

Environment Variable Overrides

Any INI key can be overridden through environment variables, including special flags like GITEA_RUN_MODE or GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT.

The ConfigProvider implementation in modules/setting/config_provider.go automatically honors these overrides. When code calls methods like setting.CfgProvider.Section("server").Key("DOMAIN").MustString("localhost"), the underlying INI handler checks for corresponding environment variables first, allowing containerized deployments to inject configuration without modifying files.

Database-Backed Dynamic Settings

Runtime-editable values—including site-wide options, UI banners, and maintenance mode—are stored in the system_setting table. This layer enables administrators to change behavior through the web UI without restarting the service.

The dbConfigCachedGetter in models/system/setting.go implements the config.DynKeyGetter interface. It reads from the database, caches the revision to minimize queries, and exposes values via setting.NewDatabaseDynKeyGetter(). Dynamic settings take precedence over static INI values when accessed through the typed configuration API.

Core Implementation Architecture

Four primary source files coordinate Gitea's settings management, each handling a distinct responsibility.

INI-Based Provider (modules/setting/config_provider.go)

This file implements the ConfigProvider, ConfigSection, and ConfigKey interfaces. It loads the INI file while disabling save-by-default behavior to prevent "MustXxx" calls from polluting the configuration file. The provider supports re-initialization for clean saves and handles the environment-variable override logic internally.

Global Setting Singleton (modules/setting/setting.go)

The global singleton stores the provider instance in setting.CfgProvider and parses common sections into exported variables like setting.AppVer and setting.RunMode. This file also defines LoadCommonSettings, which orchestrates the initialization sequence and makes static configuration available throughout the application.

Database Dynamic Key Getter (models/system/setting.go)

This model layer defines the system_setting table schema and provides GetAllSettings and SetSettings functions. The dbConfigCachedGetter struct wraps database access with revision caching, ensuring that high-frequency configuration reads do not overwhelm the database connection pool.

Strongly-Typed Config Wrapper (modules/setting/config.go)

Building on the generic machinery, this file constructs strongly-typed options like Picture.DisableGravatar, Repository.OpenWithEditorApps, and Instance.MaintenanceMode. When an option is marked as dynamic, the wrapper delegates to the database getter; otherwise, it reads from the static INI provider.

Working with Gitea Settings in Code

Access Gitea configuration through two primary APIs: the raw CfgProvider for INI values, or Config() for typed options that respect the dynamic/static distinction.

Initializing the Configuration Provider

import "code.gitea.io/gitea/modules/setting"

func initConfig() {
    // Loads the default app.ini (or the file supplied with --config)
    setting.InitCfgProvider(setting.CustomConf)
    // Parse the common settings (run mode, server, SSH, etc.)
    setting.LoadCommonSettings()
}

Accessing Static INI Values

import "code.gitea.io/gitea/modules/setting"

host := setting.CfgProvider.Section("server").Key("DOMAIN").MustString("localhost")
fmt.Println("Server domain:", host)

Using Typed Configuration Options

import "code.gitea.io/gitea/modules/setting"

if setting.Config().Picture.DisableGravatar.MustBool() {
    fmt.Println("Gravatars are disabled")
}

Reading Database-Backed Dynamic Settings

import "code.gitea.io/gitea/modules/setting"

if setting.Config().Instance.MaintenanceMode.MustBool() {
    fmt.Println("The site is currently in maintenance mode")
}

Updating Dynamic Settings Programmatically

import (
    "code.gitea.io/gitea/models/system"
    "context"
)

func enableMaintenance(ctx context.Context) error {
    // Direct DB update via the system_setting model
    return system.SetSettings(ctx, map[string]string{
        "instance.maintenance_mode": "true",
    })
}

Key Source Files and Responsibilities

File Responsibility
modules/setting/setting.go Global singleton, loads app.ini, exposes CfgProvider and top-level variables
modules/setting/config_provider.go INI-backed ConfigProvider implementation with environment override support
modules/setting/config.go Strongly-typed options built on config.Option machinery
models/system/setting.go Database table system_setting, getters/setters, and cached getter implementation
modules/setting/path.go Determines work paths (AppWorkPath, CustomPath) and invokes InitCfgProvider

Summary

  • Gitea settings use a three-layer architecture: static app.ini files for defaults, environment variables for deployment-specific overrides, and the system_setting database table for runtime changes.
  • Static configuration is managed by modules/setting/config_provider.go and exposed through modules/setting/setting.go as package-level variables.
  • Dynamic configuration is handled by models/system/setting.go, which provides a cached getter that integrates with the typed options in modules/setting/config.go.
  • Access patterns: Use setting.CfgProvider for raw INI access, or setting.Config() for type-safe options that automatically route to the database when marked dynamic.

Frequently Asked Questions

Where is the main Gitea configuration file located?

By default, Gitea looks for app.ini in the custom/conf/ directory relative to the working path. You can specify a custom location using the --config flag when starting the binary, which sets setting.CustomConf before InitCfgProvider is called.

Can I override Gitea settings without restarting the service?

Yes, but only for settings stored in the system_setting database table. Static INI changes require a restart to reload. Dynamic settings—such as maintenance mode or UI banners—can be updated via the admin web UI or programmatically through system.SetSettings(), and changes take effect immediately due to the dbConfigCachedGetter revision caching.

How do environment variables map to INI configuration keys?

Environment variables override INI keys automatically when using the ConfigProvider interface. The mapping follows standard INI conventions where variables like GITEA__SERVER__DOMAIN would override the DOMAIN key in the [server] section. Special flags like GITEA_RUN_MODE are also checked directly by the initialization code in modules/setting/setting.go.

What is the difference between static and dynamic settings in Gitea?

Static settings are read from app.ini at startup and stored in memory as package variables; they require a restart to change. Dynamic settings are stored in the database's system_setting table, read through the dbConfigCachedGetter, and can be modified at runtime. The modules/setting/config.go wrapper determines the source based on whether the option is registered as dynamic in the code.

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 →