How to Customize Gitea Settings Programmatically: 3 Methods Explained

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. 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:

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 invokes setting.NewConfigProviderFromFile(appIniPath) (implemented in the same file), which parses 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 (lines 58-63) shows these operations:

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 directly, use the NewConfigProviderFromFile constructor and persist changes with Save().

Modifying Configuration Sections

Load the provider, retrieve sections, and update keys:

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) 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 and routers/api/v1/admin/config.go (routed through 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. Authenticated users can update their settings via PATCH /user/settings:

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:

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 to the database and are managed through the admin panel or admin API. The setting.deprecatedSettingDB function (lines 44-48 of config_provider.go) handles the deprecation warnings.

When a key is present in 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 to load, modify, and save 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 require a restart to take effect because they are loaded into global variables during initialization in 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.

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 →