Configuration Reference
June 3, 2026 ยท View on GitHub
GARM is configured via a single TOML file, typically at /etc/garm/config.toml. This reference covers every section.
Minimal configuration
[default]
enable_webhook_management = true
[logging]
enable_log_streamer = true
log_format = "text"
log_level = "info"
log_source = false
[metrics]
enable = true
disable_auth = false
[jwt_auth]
secret = "<random-string-32-chars-or-more>"
time_to_live = "8760h"
[apiserver]
bind = "0.0.0.0"
port = 9997
use_tls = false
[apiserver.webui]
enable = true
[database]
backend = "sqlite3"
passphrase = "<random-32-char-string>"
[database.sqlite3]
db_file = "/etc/garm/garm.db"
[default]
| Option | Default | Description |
|---|---|---|
enable_webhook_management | false | Allow GARM to install/manage webhooks automatically |
debug_server | false | Enable the Go pprof profiling server on 127.0.0.1:9997 |
When debug_server is enabled, you can profile GARM using:
go tool pprof http://127.0.0.1:9997/debug/pprof/profile?seconds=120
Important
The profiling command will block for the duration of the profile (e.g. 120 seconds). Most reverse proxies timeout after ~60 seconds, so always profile by connecting directly to GARM on localhost. It's advisable to exclude the debug server URLs from your reverse proxy entirely.
Deprecated: callback_url, metadata_url, log_file, enable_log_streamer -- these are now managed via garm-cli controller update or the [logging] section.
[logging]
| Option | Default | Description |
|---|---|---|
log_file | (stdout) | Path to log file. Omit to log to stdout. |
enable_log_streamer | false | Enable live log streaming via WebSocket (garm-cli debug-log) |
log_format | "text" | "text" or "json". Use JSON for log aggregation (Loki, ELK). |
log_level | "info" | "debug", "info", "warn", or "error" |
log_source | false | Include source file/line in log output |
Log rotation: GARM auto-rotates at 500 MB or 28 days, whichever comes first. Send SIGHUP to manually rotate. If running under systemd, add the following to your unit file:
[Service]
ExecReload=/bin/kill -HUP $MAINPID
Then rotate with systemctl reload garm.
[database]
| Option | Default | Description |
|---|---|---|
debug | false | Log all database queries |
backend | "sqlite3" | Database backend: "sqlite3" or "postgresql" |
passphrase | (required) | 32-character string used to encrypt secrets at rest (AES-256). Protects webhook secrets, tokens, and private keys stored in the database. |
[database.sqlite3]
| Option | Default | Description |
|---|---|---|
db_file | (required) | Path to the SQLite database file |
[database.postgresql]
| Option | Default | Description |
|---|---|---|
username | (required) | PostgreSQL user |
password | (required) | PostgreSQL password |
hostname | (required) | Host or IP of the PostgreSQL server |
port | 5432 | Port the server listens on |
database | (required) | Database name |
sslmode | "prefer" | SSL mode: disable, allow, prefer, require, verify-ca, or verify-full |
max_open_conns | 25 | Maximum number of open connections in the pool |
max_idle_conns | 5 | Maximum number of idle connections in the pool |
conn_max_lifetime_mins | 30 | Maximum connection lifetime in minutes |
conn_max_idle_time_secs | 300 | Maximum time a connection may sit idle before being closed, in seconds |
[database]
backend = "postgresql"
passphrase = "<random-32-char-string>"
[database.postgresql]
username = "garm"
password = "<password>"
hostname = "localhost"
port = 5432
database = "garm"
sslmode = "prefer"
Reclaiming storage from deleted file objects
File object content can be large. Both backends reclaim the underlying storage automatically, but the mechanics differ.
SQLite. Blob content is stored in a separate database file (auto-vacuum is enabled in the connection string). GARM runs PRAGMA incremental_vacuum against both the main and objects databases every 24 hours, so freed pages are returned to the filesystem without operator intervention.
PostgreSQL. Blob content is stored in PostgreSQL Large Objects (pg_largeobject). When a file object is deleted, GARM calls lo_unlink inline in the same transaction, and PostgreSQL's autovacuum reclaims the underlying storage.
If lo_unlink fails during deletion (for example because of a transient connection error), GARM logs the orphaned OID at error level. To recover such orphans, run the vacuumlo utility (shipped with PostgreSQL):
vacuumlo -U garm -W garm
[[provider]]
Providers are external executables that GARM calls to create and manage runner instances. You can define multiple providers.
[[provider]]
name = "lxd_local"
provider_type = "external"
description = "Local LXD installation"
[provider.external]
provider_executable = "/opt/garm/providers.d/garm-provider-lxd"
config_file = "/etc/garm/garm-provider-lxd.toml"
environment_variables = ["AWS_"]
| Option | Description |
|---|---|
name | Unique name for this provider |
provider_type | Always "external" |
description | Human-readable description |
provider_executable | Absolute path to the provider binary |
config_file | Path to the provider's own config file |
environment_variables | List of env var names or prefixes to pass to the provider (e.g., ["AWS_"] passes all AWS_* vars) |
[metrics]
| Option | Default | Description |
|---|---|---|
enable | false | Enable the /metrics Prometheus endpoint |
disable_auth | false | Disable JWT authentication on the metrics endpoint |
period | "60s" | How often internal metrics are recalculated |
To generate a metrics token for Prometheus:
garm-cli metrics-token create
See Monitoring and Debugging for Prometheus configuration.
[jwt_auth]
| Option | Default | Description |
|---|---|---|
secret | (required) | Secret used to sign JWT tokens. Use a long, random string. |
time_to_live | "24h" | How long admin tokens are valid. Minimum 24h. |
This TTL applies only to tokens you get when logging into the API (and metrics tokens). Tokens issued to runner instances have a TTL based on the pool's bootstrap timeout plus the GARM polling interval -- they are not affected by this setting.
Changing the secret invalidates all existing tokens.
[apiserver]
| Option | Default | Description |
|---|---|---|
bind | "0.0.0.0" | IP address to bind to |
port | 9997 | Port to listen on |
use_tls | false | Enable TLS on the API server |
cors_origins | [] | Allowed CORS origins. ["*"] allows all. |
[apiserver.tls]
| Option | Description |
|---|---|
certificate | Path to x509 certificate (or full chain bundle) |
key | Path to the private key |
Important
If your certificate is signed by an intermediary CA, the certificate file must contain the entire chain (your certificate concatenated with the CA bundle). Without the full chain, clients may fail to validate the connection.
While GARM supports TLS natively, using a reverse proxy (e.g. nginx) for TLS termination is recommended for most production setups.
[apiserver.webui]
| Option | Default | Description |
|---|---|---|
enable | true | Enable the Web UI at /ui/ |
Available providers
| Provider | Repository |
|---|---|
| Akamai/Linode | flatcar/garm-provider-linode (experimental) |
| Amazon EC2 | cloudbase/garm-provider-aws |
| Azure | cloudbase/garm-provider-azure |
| CloudStack | nexthop-ai/garm-provider-cloudstack |
| GCP | cloudbase/garm-provider-gcp |
| Incus | cloudbase/garm-provider-incus |
| Kubernetes | mercedes-benz/garm-provider-k8s |
| LXD | cloudbase/garm-provider-lxd |
| OpenStack | cloudbase/garm-provider-openstack |
| Oracle OCI | cloudbase/garm-provider-oci |
Each provider has its own configuration file and documentation. Refer to the provider's repository for setup instructions and available extra_specs options.