Skip to content

Declarative Provisioning

Logchef supports declarative provisioning: define your teams, data sources, and access control in a TOML config file instead of (or alongside) the web UI. This enables GitOps workflows where infrastructure config is version-controlled and deployed automatically.

Provisioning uses a managed vs unmanaged strategy:

  • Managed resources: Declared in your provisioning config. Logchef creates, updates, and optionally deletes them on startup. The API rejects manual edits to managed resources.
  • Unmanaged resources: Created via the UI. Provisioning ignores them entirely.

This means you can gradually adopt provisioning: start by declaring a few sources, and leave everything else UI-managed.

manage_sources = true
manage_teams = true
prune = false
dry_run = true # Start with dry-run to verify
[[sources]]
name = "Production Logs"
source_type = "clickhouse"
meta_ts_field = "timestamp"
description = "Production application logs"
ttl_days = 30
[sources.connection]
host = "clickhouse.internal:9000"
username = "logchef"
password = "secret"
database = "logs"
table_name = "otel_logs"
[[teams]]
name = "Platform"
description = "Platform engineering"
sources = ["Production Logs"]
members = [
{ email = "alice@example.com", role = "admin" },
{ email = "bob@example.com", role = "editor" },
{ email = "carol@example.com", role = "member" },
]
[provisioning]
file = "provisioning.toml"

On startup, Logchef reconciles the declared state with the database. With dry_run = true, it logs what it would do without making changes:

INFO provisioning dry-run complete, rolling back transaction

Once satisfied with the dry-run output, set dry_run = false and restart.

Field Type Default Description
manage_sources bool false Enable declarative source management
manage_teams bool false Enable declarative team management
prune bool false Delete managed resources removed from config
dry_run bool false Log changes without applying them
Field Required Default Description
name Yes — Unique display name (used as identity key)
source_type No clickhouse Datasource backend: clickhouse or victorialogs
connection Yes for new configs — Provider-specific connection block
secret_ref No — Environment variable name for the provider secret
meta_ts_field No timestamp for ClickHouse, _time for VictoriaLogs Timestamp field name
meta_severity_field No — Severity/level column name
description No — Human-readable description
ttl_days No 0 Data retention in days

For ClickHouse, the connection block looks like:

[[sources]]
name = "Production Logs"
source_type = "clickhouse"
meta_ts_field = "timestamp"
secret_ref = "LOGCHEF_CH_PROD_PASSWORD"
[sources.connection]
host = "clickhouse.internal:9000"
username = "logchef"
database = "logs"
table_name = "otel_logs"

For VictoriaLogs, use the native API connection shape:

[[sources]]
name = "Payments Logs"
source_type = "victorialogs"
meta_ts_field = "_time"
meta_severity_field = "level"
secret_ref = "LOGCHEF_VL_PROD_TOKEN"
[sources.connection]
base_url = "https://logs.example.com"
[sources.connection.auth]
mode = "bearer"
[sources.connection.tenant]
account_id = "12"
project_id = "34"
[sources.connection.scope]
query = "{app=\"payments\"} kubernetes.namespace:=prod"

The provisioning format is now nested and datasource-native. ClickHouse sources must use source_type plus the nested connection block rather than top-level host / database / table_name fields.

Field Required Description
name Yes Unique display name (used as identity key)
description No Human-readable description
sources No List of source names this team can access
members No List of team members
Field Required Default Description
email Yes — User’s email (must match OIDC identity)
role No member Team role: admin, editor, or member

Never commit passwords to version control. Use secret_ref to reference environment variables:

[[sources]]
name = "Production Logs"
source_type = "clickhouse"
secret_ref = "LOGCHEF_CH_PROD_PASSWORD"
[sources.connection]
host = "clickhouse:9000"
username = "logchef"
database = "logs"
table_name = "otel_logs"

Set the environment variable before starting Logchef:

Terminal window
export LOGCHEF_CH_PROD_PASSWORD="actual-password"

For VictoriaLogs bearer auth, secret_ref fills connection.auth.token. For VictoriaLogs basic auth, it fills connection.auth.password.

For Nomad deployments, use Nomad variables with template blocks:

password = "{{ "{{.ch_prod_password}}" }}"

When you first enable provisioning on an existing Logchef instance, resources declared in config are matched against existing database records by name:

  • A source named “Production Logs” in config matches an existing source named “Production Logs” in the DB
  • Matched resources are adopted (marked as managed) and updated to match config
  • Unmatched config entries create new resources

No special migration flag is needed. Adoption happens automatically on name match.

With prune = false (default), managed resources removed from config are kept but logged as warnings:

WARN managed source not in config (prune=false, keeping) name="Old Source"

With prune = true, they are deleted. Be careful:

Managed resources cannot be modified via the API or UI. Attempting to edit or delete a managed team/source/user returns:

{
"status": "error",
"message": "This source is managed by provisioning config and cannot be modified via API",
"error_type": "ManagedResourceError"
}

Admins can export the current database state as a provisioning config via the API:

Terminal window
curl -s https://logchef.example.com/api/v1/admin/provisioning/export \
-H "Authorization: Bearer <token>" | jq .

This returns a JSON representation of all sources, teams, and memberships, useful as a starting point for writing your provisioning.toml.

You can define provisioning inline in config.toml or as a separate file:

Separate file (recommended):

config.toml
[provisioning]
file = "provisioning.toml"

Inline:

config.toml
[provisioning]
manage_sources = true
manage_teams = true
[[provisioning.sources]]
name = "My Logs"
source_type = "clickhouse"
[provisioning.sources.connection]
# ...

The separate file approach keeps secrets isolated and makes the provisioning config independently deployable.