# How to Customize Gitea Settings Programmatically: 3 Methods Explained

> Discover how to customize Gitea settings programmatically using ConfigProvider, REST API, or admin API. Learn three effective methods for managing your Gitea configuration.

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

---

**You can customize Gitea settings programmatically through the `ConfigProvider` interface for direct file manipulation, REST API endpoints for runtime changes, or the admin API for database-backed configuration values.**

Gitea's configuration system is built on a layered, INI-based architecture that supports both file-based modifications and runtime updates. According to the go-gitea/gitea source code, administrators can automate configuration changes using Go's native `setting` package or external REST calls without requiring manual UI interaction.

## Understanding the ConfigProvider Architecture

At the core of Gitea's configuration system is the `ConfigProvider` interface defined in [`modules/setting/config_provider.go`](https://github.com/go-gitea/gitea/blob/main/modules/setting/config_provider.go). This abstraction wraps INI file operations and exposes methods for reading and mutating settings programmatically.

### The ConfigProvider Interface

The interface defines the contract for all configuration operations:

```go
type ConfigProvider interface {
    Section(string) ConfigSection      // retrieve a section
    Save() error                      // persist changes
    DisableSaving()                   // make the provider read-only
    PrepareSaving() (ConfigProvider, error) // reload clean config for safe saving
}

```

When Gitea starts, [`cmd/web.go`](https://github.com/go-gitea/gitea/blob/main/cmd/web.go) invokes `setting.NewConfigProviderFromFile(appIniPath)` (implemented in the same file), which parses [`app.ini`](https://github.com/go-gitea/gitea/blob/main/app.ini) or a custom file supplied with the `-c` flag. The provider is stored globally in `setting.CfgProvider`.

### Reading and Writing Values

To access settings programmatically, obtain a section via `provider.Section("section_name")`, then manipulate keys using `ConfigSection.Key("key")`. The returned `ConfigKey` supports type-safe getters like `MustString`, `MustBool`, and `MustInt`, while `SetValue(v)` updates values in memory.

The implementation in [`modules/setting/config_provider.go`](https://github.com/go-gitea/gitea/blob/main/modules/setting/config_provider.go) (lines 58-63) shows these operations:

```go
func (s *iniConfigSection) Key(key string) ConfigKey { return s.sec.Key(key) }
func (s *iniConfigSection) DeleteKey(key string) { s.sec.DeleteKey(key) }

```

## Method 1: Direct ConfigProvider Manipulation

For deployment scripts or external Go tools that need to modify [`app.ini`](https://github.com/go-gitea/gitea/blob/main/app.ini) directly, use the `NewConfigProviderFromFile` constructor and persist changes with `Save()`.

### Modifying Configuration Sections

Load the provider, retrieve sections, and update keys:

```go
package main

import (
    "log"

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

func main() {
    // Load the existing configuration file
    provider, err := setting.NewConfigProviderFromFile("custom/app.ini")
    if err != nil {
        log.Fatalf("load config: %v", err)
    }

    // Change a global UI setting
    uiSec := provider.Section("ui")
    uiSec.Key("DEFAULT_THEME").SetValue("gitea-dark")

    // Enable a feature flag
    featureSec := provider.Section("service")
    featureSec.Key("ENABLE_NOTIFY_MAIL").SetValue("true")

    // Persist changes
    if err := provider.Save(); err != nil {
        log.Fatalf("save config: %v", err)
    }
}

```

### Persistence and Permissions

The `Save()` method (lines 66-93 of [`config_provider.go`](https://github.com/go-gitea/gitea/blob/main/config_provider.go)) writes the modified INI back to disk and ensures file permissions are tightened for security. If the provider was created from a temporary source, use `SaveTo(filename)` instead.

## Method 2: REST API for Runtime Configuration

Gitea exposes **REST endpoints** that allow external clients to read or modify settings without file system access, implemented in [`routers/api/v1/settings/settings.go`](https://github.com/go-gitea/gitea/blob/main/routers/api/v1/settings/settings.go) and [`routers/api/v1/admin/config.go`](https://github.com/go-gitea/gitea/blob/main/routers/api/v1/admin/config.go) (routed through [`routers/api/v1/api.go`](https://github.com/go-gitea/gitea/blob/main/routers/api/v1/api.go) at lines 958-962).

### Global System Settings

Access global UI, API, repository, and attachment settings via endpoints like `GET /settings/ui` or `GET /settings/api`. Changes made through these endpoints read directly from the loaded `setting` structures and reflect immediately in API responses.

### Per-User Settings

User-specific preferences are handled in [`routers/api/v1/user/settings.go`](https://github.com/go-gitea/gitea/blob/main/routers/api/v1/user/settings.go). Authenticated users can update their settings via `PATCH /user/settings`:

```bash
USER_TOKEN="USER_ACCESS_TOKEN"

curl -X PATCH "https://git.example.com/api/v1/user/settings" \
     -H "Authorization: token $USER_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"theme": "gitea-dark"}'

```

### Admin Configuration Endpoint

For system-wide changes while the server is running, use the admin API with an admin token:

```bash
TOKEN="YOUR_ADMIN_TOKEN"

curl -X PATCH "https://git.example.com/api/v1/admin/config" \
     -H "Authorization: token $TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"ui": {"DEFAULT_THEME": "gitea-dark"}}'

```

## Method 3: Database-Backed Settings

Certain configuration options have migrated from [`app.ini`](https://github.com/go-gitea/gitea/blob/main/app.ini) to the database and are managed through the admin panel or admin API. The `setting.deprecatedSettingDB` function (lines 44-48 of [`config_provider.go`](https://github.com/go-gitea/gitea/blob/main/config_provider.go)) handles the deprecation warnings.

When a key is present in [`app.ini`](https://github.com/go-gitea/gitea/blob/main/app.ini) but managed in the database, it is ignored. Update these values via the **admin API** (`/admin/config`) or the web UI at *Configuration → Settings*.

## Summary

- **ConfigProvider Interface**: Use [`modules/setting/config_provider.go`](https://github.com/go-gitea/gitea/blob/main/modules/setting/config_provider.go) to load, modify, and save [`app.ini`](https://github.com/go-gitea/gitea/blob/main/app.ini) programmatically from Go code.
- **REST API**: Call `/settings/*`, `/user/settings`, or `/admin/config` endpoints to change settings at runtime without file access.
- **Database Storage**: Modern Gitea versions store some settings in the database; modify these via the admin API rather than the INI file.
- **File Permissions**: The `Save()` method automatically tightens permissions when writing configuration files.
- **Runtime Reflection**: API endpoints read from the same `setting` structures populated at startup, ensuring consistency between file and API-based configuration.

## Frequently Asked Questions

### Can I change Gitea settings without restarting the server?

Yes. Use the **REST API** endpoints (`/admin/config` for system settings or `/user/settings` for personal preferences) to modify values at runtime. However, some low-level settings in [`app.ini`](https://github.com/go-gitea/gitea/blob/main/app.ini) require a restart to take effect because they are loaded into global variables during initialization in [`modules/setting/setting.go`](https://github.com/go-gitea/gitea/blob/main/modules/setting/setting.go).

### What happens if I edit app.ini settings that have moved to the database?

Gitea ignores INI values for settings that have been migrated to the database. The `setting.deprecatedSettingDB` function logs a warning when such keys are detected. You must update these values via the admin panel or the admin API (`/admin/config`) instead of editing the configuration file.

### Is the ConfigProvider safe for concurrent modifications?

The `ConfigProvider` interface does not guarantee thread safety for concurrent writes. For automation scripts, ensure only one process calls `Save()` at a time. For runtime modifications, prefer the **REST API** which handles concurrency through Gitea's HTTP request handlers.

### How do I access configuration values in an external tool?

Import `code.gitea.io/gitea/modules/setting` and use `NewConfigProviderFromFile()` to load the configuration outside of Gitea itself. This approach is useful for deployment automation that needs to verify or modify settings before starting the Gitea process.