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

> Discover where Gitea settings are managed across INI files environment variables and database tables. Understand Gitea configuration layers for seamless management.

- Repository: [Gitea/gitea](https://github.com/go-gitea/gitea)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Gitea settings are managed across three complementary layers: a static INI configuration file ([`app.ini`](https://github.com/go-gitea/gitea/blob/main/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`](https://github.com/go-gitea/gitea/blob/main/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`](https://github.com/go-gitea/gitea/blob/main/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`](https://github.com/go-gitea/gitea/blob/main/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`](https://github.com/go-gitea/gitea/blob/main/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`](https://github.com/go-gitea/gitea/blob/main/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`](https://github.com/go-gitea/gitea/blob/main/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`](https://github.com/go-gitea/gitea/blob/main/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`](https://github.com/go-gitea/gitea/blob/main/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

```go
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

```go
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

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

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

```

### Reading Database-Backed Dynamic Settings

```go
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

```go
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`](https://github.com/go-gitea/gitea/blob/main/modules/setting/setting.go) | Global singleton, loads [`app.ini`](https://github.com/go-gitea/gitea/blob/main/app.ini), exposes `CfgProvider` and top-level variables |
| [`modules/setting/config_provider.go`](https://github.com/go-gitea/gitea/blob/main/modules/setting/config_provider.go) | INI-backed `ConfigProvider` implementation with environment override support |
| [`modules/setting/config.go`](https://github.com/go-gitea/gitea/blob/main/modules/setting/config.go) | Strongly-typed options built on `config.Option` machinery |
| [`models/system/setting.go`](https://github.com/go-gitea/gitea/blob/main/models/system/setting.go) | Database table `system_setting`, getters/setters, and cached getter implementation |
| [`modules/setting/path.go`](https://github.com/go-gitea/gitea/blob/main/modules/setting/path.go) | Determines work paths (`AppWorkPath`, `CustomPath`) and invokes `InitCfgProvider` |

## Summary

- **Gitea settings use a three-layer architecture**: static [`app.ini`](https://github.com/go-gitea/gitea/blob/main/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`](https://github.com/go-gitea/gitea/blob/main/modules/setting/config_provider.go) and exposed through [`modules/setting/setting.go`](https://github.com/go-gitea/gitea/blob/main/modules/setting/setting.go) as package-level variables.
- **Dynamic configuration** is handled by [`models/system/setting.go`](https://github.com/go-gitea/gitea/blob/main/models/system/setting.go), which provides a cached getter that integrates with the typed options in [`modules/setting/config.go`](https://github.com/go-gitea/gitea/blob/main/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`](https://github.com/go-gitea/gitea/blob/main/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`](https://github.com/go-gitea/gitea/blob/main/modules/setting/setting.go).

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

**Static settings** are read from [`app.ini`](https://github.com/go-gitea/gitea/blob/main/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`](https://github.com/go-gitea/gitea/blob/main/modules/setting/config.go) wrapper determines the source based on whether the option is registered as dynamic in the code.