Configuration reference
cronpad reads settings from built-in defaults, then an optional TOML file (cronpad.toml in the working directory, or --config <path>), then environment variables, then command-line flags; later sources win. Any setting can be set by environment variable: prefix CRONPAD_, uppercase the key, and join nested sections with a double underscore. Invalid configuration stops the server at startup with a list of every problem.
[database]
path = "/data/cronpad.db"
is the same as
CRONPAD_DATABASE__PATH=/data/cronpad.db
Durations use Go units (ms, s, m, h) plus d for days: "500ms", "30s", "1h30m", "30d". Lists in environment variables are comma-separated: CRONPAD_AUTH__ALLOWED_EMAIL_DOMAINS=example.com,example.org.
Top level
| Key | Default | Description |
|---|---|---|
mode | "selfhosted" | selfhosted or hosted. hosted applies safe defaults for a public, multi-tenant server; see Security. |
server
| Key | Default | Description |
|---|---|---|
addr | ":8080" | Address the process listens on. |
public_url | "http://localhost:8080" | The URL users reach cronpad at, e.g. https://cron.example.com. Used for OAuth callbacks, links in alert emails, and to mark cookies Secure when it’s https. Set it whenever cronpad is behind a proxy or on a domain. |
read_timeout | "15s" | Maximum time to read a request. |
write_timeout | "30s" | Maximum time to write a response. |
shutdown_grace | "30s" | How long SIGTERM waits for in-flight runs. Make your orchestrator’s stop timeout longer than this. |
trusted_proxies | [] | CIDRs whose X-Forwarded-For is trusted. |
ui
| Key | Default | Description |
|---|---|---|
dashboard | true | Serve the dashboard at /. false disables the dashboard; the API, /auth, /mcp and health endpoints keep working. |
cors_origins | [] | Exact origins (no wildcards, no paths; for example https://www.example.com) allowed to call /api/ from a browser. Leave empty for normal self-hosting: the dashboard and API share an origin. Set it only when another site’s browser JavaScript calls your API, such as a page embedding the schedule preview. Cookies are never sent cross-origin, so those calls reach only public endpoints or use an API key. Env: CRONPAD_UI__CORS_ORIGINS (comma-separated). |
database
| Key | Default | Description |
|---|---|---|
path | "./data/cronpad.db" | Control database: users, workspaces, jobs, keys, secrets, alert config. |
history_path | "./data/history.db" | History database: runs, attempts, audit log, alert outbox, stats. |
busy_timeout | "5s" | How long SQLite waits on a lock before failing. |
run_migrations | true | Apply migrations on startup. |
Both files must be on a local or block-storage filesystem, never NFS/SMB.
queue
| Key | Default | Description |
|---|---|---|
path | "" | The SQLite queue file. Empty means <name>-queue.db beside database.path (./data/cronpad-queue.db by default), so two servers with differently named databases in one directory never share a queue. It is a rebuildable index: never back it up or restore it. Must differ from both database paths. |
Releases that used Redis read a [redis] section; it is now ignored with a warning at startup. See Upgrading from a Redis-based version.
scheduler
See Scheduling engine for what these control.
| Key | Default | Description |
|---|---|---|
tick_interval | "100ms" | Longest the claim loop sleeps when nothing is due sooner. |
results_flush_interval | "0s" | 0s = group commit: the result writer commits as soon as it is free and batches whatever arrived meanwhile. A positive value (1ms through 1s) instead waits for a fixed ticker to grow batches. |
results_max_batch | 500 | Commit immediately at this command count; 1 through 10000. |
delivery_mode | "durable" | durable waits for admission commits; opt-in fast overlaps delivery and commits. Requires receiver idempotency; allowed in hosted mode. |
claim_batch | 500 | Maximum occurrences claimed at once. |
lease_duration | "60s" | In-flight lease. Renewed while a sink runs, so it only expires when a worker crashes or hangs. |
reaper_interval | "5s" | How often expired leases are returned to the queue. |
lock_ttl | "15s" | Dispatcher lock lifetime; renewed every third of it. |
reconcile_interval | "10s" | How often overdue jobs are checked for lost queue entries. |
overdue_grace | "30s" | How far past its fire time a job must be before it counts as overdue. |
full_reconcile_interval | "6h" | Full database ↔ queue sweep (also at startup and when queue data loss is detected). |
sla_threshold | "5s" | Default start lag above which a run breaches its SLA. Jobs can override it. |
default_misfire_policy | "fire_once" | fire_once, fire_all or skip. See Misfires. |
misfire_fire_all_cap | 10 | Maximum missed occurrences replayed by fire_all. |
workers
| Key | Default | Description |
|---|---|---|
count | 64 | Concurrent sink executions. |
queue_size | 1024 | Hand-off buffer between the claim loop and workers. |
sinks.webhook
| Key | Default | Description |
|---|---|---|
per_host_rate_limit | 20 | Hosted-only requests/second per destination host across workspaces; token bucket delays excess. 0 disables; self-hosted mode leaves it off. |
enabled | true | |
default_timeout | "30s" | Per-attempt timeout when a job doesn’t set one. |
max_timeout | "120s" | Largest timeout a job may set. |
max_response_bytes | 65536 | Response bytes read and stored per attempt. |
allow_private_networks | true (false in hosted mode) | Allow webhooks to loopback, private and link-local addresses. |
user_agent | "cronpad/{version}" |
sinks.command
| Key | Default | Description |
|---|---|---|
enabled | true (false in hosted mode) | Allow command jobs. |
shell | "/bin/sh" | Shell used when a job sets shell: true. |
allowed_commands | [] | If non-empty, only these executables may run, and shell: true is rejected. |
default_timeout | "60s" | |
max_output_bytes | 65536 | Captured stdout+stderr per attempt. |
env_passthrough | [] | Server environment variables passed to commands, besides PATH, HOME and LANG. |
retry.defaults
| Key | Default | Description |
|---|---|---|
max_attempts | 3 | Including the first attempt. |
backoff | "exponential" | exponential or fixed. |
initial_interval | "10s" | |
max_interval | "10m" | |
multiplier | 2.0 | |
jitter | 0.2 | ±fraction applied to each wait. |
auto_disable
| Key | Default | Description |
|---|---|---|
enabled | true | Disable jobs after too many consecutive failed runs. |
consecutive_failures | 20 | Default threshold. |
notify | true | Email workspace owners when a job is disabled. |
limits
0 means unlimited. Self-hosted defaults are unlimited; cronpad Cloud sets its own values.
| Key | Default | Description |
|---|---|---|
max_jobs_per_workspace | 0 | |
min_interval | "1s" | Shortest allowed time between a job’s occurrences. |
max_runs_per_month_per_workspace | 0 | UTC calendar-month cap; automatic retries do not count. |
over_quota | "block" | block records excess occurrences as skipped; allow keeps delivering and counting. |
max_concurrent_runs_per_workspace | 0 | Maximum concurrent admissions per workspace; excess claims wait 250ms. |
max_alert_emails_per_day | 0 | Ordinary alert emails per workspace per UTC day; excess is suppressed. |
max_runs_per_day_per_workspace | deprecated | Converted ×30 to monthly with a startup warning; explicit monthly wins. |
max_api_keys_per_workspace | 0 | Unlimited on self-hosted; hosted default 20. |
max_retry_attempts | 0 | Upper bound for a job’s max_attempts. |
api_rate_limit_per_minute | 600 | Per API key or session. |
retention
See Data retention. "0" keeps data forever.
| Key | Default | Description |
|---|---|---|
runs | "30d" | Runs and their attempts. |
attempt_bodies | "7d" | Response bodies and command output only. |
store_success_bodies | false | Also store bodies of successful attempts. |
audit_log | "30d" | |
alert_outbox | "30d" | Sent, failed and suppressed alerts. Pending alerts are never deleted. |
job_stats_daily | "400d" | Daily rollups behind dashboard charts. |
janitor_interval | "5m" | |
janitor_batch | 1000 | Rows deleted per transaction. |
auth
See Authentication.
| Key | Default | Description |
|---|---|---|
session_lifetime | "720h" | |
allowed_email_domains | [] | If set, only these email domains may sign in. |
allow_signup | false | true lets anyone who can sign in create an account. When false, only existing users sign in, except the first user of an empty server (First sign-in). |
dev_login | false | Passwordless local login for development. Never enable on a reachable server. |
google.client_id, google.client_secret | "" | |
github.client_id, github.client_secret | "" |
secrets
| Key | Default | Description |
|---|---|---|
encryption_key | "" | 32 random bytes, base64. Required to use secrets. Generate with openssl rand -base64 32. Keep a copy: backups are useless without it. |
alerts.email
| Key | Default | Description |
|---|---|---|
enabled | false | |
smtp_host, smtp_port | "", 587 | STARTTLS on 587, implicit TLS on 465. |
username, password | "" | |
from | "cronpad <alerts@example.com>" |
telemetry
See Observability.
| Key | Default | Description |
|---|---|---|
service_name | "cronpad" | |
otlp_endpoint | "" | OTLP collector endpoint. Empty disables OTLP export. |
otlp_protocol | "grpc" | grpc or http. |
otlp_logs | false | Also export logs over OTLP. |
prometheus_addr | "" | e.g. ":9090" to serve /metrics on a separate port. |
traces_sample_ratio | 0.1 | Fraction of runs traced. |
logging
See Logging.
| Key | Default | Description |
|---|---|---|
level | "info" | debug, info, warn, error. |
format | "json" | json or logfmt. |
add_source | false | Include source=file.go:123. |
mcp
| Key | Default | Description |
|---|---|---|
enabled | true | Serve the MCP server. |
path | "/mcp" |
Example
[server]
public_url = "https://cron.example.com"
trusted_proxies = ["10.0.0.0/8"]
[auth.github]
client_id = "Iv1.0123456789"
# client_secret from CRONPAD_AUTH__GITHUB__CLIENT_SECRET
[alerts.email]
enabled = true
smtp_host = "smtp.postmarkapp.com"
from = "cronpad <cron@example.com>"
[logging]
format = "logfmt"
[retention]
runs = "90d"
auth.oidc
One optional instance-wide OIDC login provider; see Authentication for Authentik and Keycloak setup. Environment variables use the prefix CRONPAD_AUTH__OIDC__.
| Key | Default | Description |
|---|---|---|
issuer | "" | Issuer URL, used for discovery; empty disables the provider. |
client_id | "" | OIDC client ID, required when issuer is configured. |
client_secret | "" | Confidential client secret; prefer an environment variable. |
display_name | "SSO" | Label displayed on the dashboard sign-in button. |
scopes | ["openid", "email", "profile"] | Requested scopes; must include openid. |
require_email_verified | true | Reject identities without verified email; disabling trusts the provider’s email for linking. |
Register <public_url>/auth/oidc:default/callback as the provider’s exact redirect URI. Discovery and token verification errors reject login. This provider counts toward the startup OAuth-provider requirement.