Configuration
gtcnsl treats Gitea's app.ini as declarative config: render it from a template you keep in Git, preview the diff, then apply it atomically with a backup and a health check.
Where config lives
Gitea reads /etc/gitea/app.ini. gtcnsl never edits it in place — every write goes through a backup → atomic-write → restart → health-check cycle, and rolls back to app.ini.bak if Gitea fails to come back up.
Read the current config
# only the keys that differ from Gitea's defaults
gtcnsl config get
# every schema key, with the default shown beside each override
gtcnsl config get --show-defaults
# one key, pipe-friendly
gtcnsl config get server.DOMAIN
config get is read-only. Header-less prologue keys (rare, e.g. RUN_USER) are queried without a dot.
Apply a template (the declarative path)
Keep an app.ini.tmpl with ${VAR} placeholders under version control, then render and apply it:
gtcnsl config apply \
--template app.ini.tmpl \
--var DOMAIN=git.example.com \
--var ROOT_URL=https://git.example.com/ \
--dry-run
# happy with the diff? drop --dry-run and confirm
gtcnsl config apply --template app.ini.tmpl --var DOMAIN=git.example.com --yes
A ${VAR} with no matching --var is an error, not an empty string — so a typo fails loudly instead of silently blanking a value. Write \${VAR} to emit a literal ${VAR}.
--dry-run computes the exact diff and writes nothing. Make it a habit before any apply.Change a single key
gtcnsl config set server.DOMAIN git.example.com --yes
gtcnsl config toggle service.DISABLE_REGISTRATION --yes
set writes one key; toggle flips a boolean (true ↔ false). Both reuse the same backup-and-rollback cycle as apply.
What you cannot set this way
config set / toggle refuse secret keys (e.g. security.SECRET_KEY, security.INTERNAL_TOKEN, oauth2.JWT_SECRET) and compound keys that change subsystem shape (e.g. database.DB_TYPE, session.PROVIDER, cache.ADAPTER). Manage secrets with gtcnsl secrets instead.
Sync the schema catalog
To diff against a specific Gitea version's defaults, cache its app.example.ini:
gtcnsl config sync-schema --version 1.26.2 --yes
It downloads (~50 MB) and caches to /var/lib/gtcnsl/schema-cache/<version>/. Add --refresh to re-download.
apply/set/toggle restarts gitea.service — expect a few seconds of downtime. If the health check fails, gtcnsl restores app.ini.bak and restarts again.The runner's config.yaml
gitea-runner's own settings (runner.post_task_script, action_shallow_clone, container.network_create_options.enable_ipv4/6, and everything else generate-config documents) are a separate file from app.ini, and not covered by config apply/set/toggle above. runner register/reconfigure write a baseline the first time — via the installed binary's own generate-config subcommand, never overwriting one that's already there. To (re)generate it explicitly:
gtcnsl runner config generate # refuses if config.yaml already exists
gtcnsl runner config generate --force # backs up the existing file first, then overwrites
The file lives at <WorkDir>/config.yaml (/var/lib/gitea-runner/config.yaml by default), mode 0640. Declarative apply of individual keys — the config apply equivalent for the runner — isn't there yet; runner config is the parent command reserved for it.
Next steps
Keep app.ini.tmpl in Git and let config apply reconcile drift. For the four managed secrets, see the Secrets guide.