# Production checklist

Go through this list before you rely on a self-hosted cronpad. Each item prevents a specific failure: lost data, duplicate scheduling, silent outages or leaked secrets.

## Data

- [ ] **One cronpad process per data directory.** Kubernetes: `replicas: 1`, `strategy: Recreate`. Two processes on one SQLite file can corrupt it. (A second process started by mistake waits in standby, because the dispatcher lock lives in the queue file, but don't rely on it.)
- [ ] **Local or block storage**, never NFS/SMB/EFS for the database and queue files. Keep the queue file (`cronpad-queue.db`) on the same volume, and leave it out of any backup or restore.
- [ ] **Backups in place.** cronpad runs on [plain SQLite](backups.md#sqlite-only-the-default) unless you turn replication on, so pick one: Litestream (`LITESTREAM_REPLICA_URL` set) with a [tested restore](backups.md#test-your-restores), or scheduled `sqlite3 .backup` of both databases, or volume snapshots.
- [ ] **`secrets.encryption_key` set** from your secret manager, with a copy kept outside the server.
- [ ] **Retention** set for your needs and disk size ([Data retention](../platform/data-retention.md)).

## Access

- [ ] `server.public_url` is the real `https://` URL, so cookies are `Secure` and OAuth callbacks work.
- [ ] At least one OAuth provider configured; `auth.dev_login` is `false`.
- [ ] You signed in first. On an empty server the first sign-in creates an account even with sign-up off, so sign in before exposing the server, or set `auth.allowed_email_domains` ([First sign-in](authentication.md#first-sign-in)).
- [ ] `auth.allow_signup` left `false` (the default), or, if you turned it on, `auth.allowed_email_domains` set to your organization's domains.
- [ ] Commands restricted with `sinks.command.allowed_commands`, or disabled if you don't use them.
- [ ] TLS terminated at your proxy or load balancer, with `server.trusted_proxies` set.

## Operations

- [ ] Liveness probe on `/healthz`, readiness probe and external uptime check on `/readyz`.
- [ ] Stop timeout longer than `server.shutdown_grace` (default 30 s), so in-flight runs finish on deploy.
- [ ] Metrics scraped or exported ([Observability](../observability/index.md)), with alerts on at least:
  - `/readyz` failing;
  - `cronpad.queue.oldest_due_age` above 30 s (work is waiting);
  - `cronpad.leases.expired` increasing (workers crashing or hanging);
  - SLA breach ratio above 1%.
- [ ] Logs collected, in `json` for a log pipeline or `logfmt` for humans.
- [ ] Alert email (SMTP) configured, so auto-disable notifications reach someone.

## Expectations

- A deploy or restart pauses scheduling for a few seconds. Runs due during it start late and are flagged as SLA breaches; none are lost.
- Receivers should be idempotent ([Delivery guarantees](../concepts/delivery-guarantees.md)).