Self-host cronpad
This tutorial runs cronpad on one machine with Docker Compose, configures GitHub sign-in, and creates a first job. For production hardening, read the production checklist afterwards.
Before you start
- Docker with the Compose plugin.
- A GitHub account (to create an OAuth app), or use development login for a local trial.
1. Get the Compose file
mkdir cronpad && cd cronpad
curl -fsSLO https://raw.githubusercontent.com/gowthamgts/cronpad/main/deploy/docker-compose.yml
It starts one container, cronpad (port 8080), with everything it stores in the cronpad-data volume. There is no database or queue server to run.
2. Configure sign-in
For a quick local trial, enable development login (never use this on a server reachable by others):
cat > .env <<'ENV'
CRONPAD_AUTH__DEV_LOGIN=true
ENV
For real use, create a GitHub OAuth app with callback URL http://localhost:8080/auth/github/callback and put its credentials in .env:
cat > .env <<'ENV'
CRONPAD_SERVER__PUBLIC_URL=http://localhost:8080
CRONPAD_AUTH__GITHUB__CLIENT_ID=Iv1.xxxxxxxx
CRONPAD_AUTH__GITHUB__CLIENT_SECRET=xxxxxxxx
ENV
Google works the same way. See Authentication.
Create a persistent encryption key before saving jobs, including with development
login, and tell Compose to pass .env into the server:
printf 'CRONPAD_SECRETS__ENCRYPTION_KEY=%s\n' "$(openssl rand -base64 32)" >> .env
cat > docker-compose.override.yml <<'YAML'
services:
cronpad:
env_file: .env
YAML
Keep that encryption key when restarting or upgrading. See Secrets for rotation.
3. Start it
docker compose up -d
docker compose logs -f cronpad
Wait for the server started log line, then open http://localhost:8080. A new server opens on a setup page: sign in to create the first account, and you land on the dashboard at /. Sign-up then closes; see First sign-in to let others in.
4. Schedule a job
Create a webhook job in the dashboard at /jobs/new, using an HTTPS
endpoint you control. The default Compose service disables the command sink
unless you set CRONPAD_SINKS__COMMAND__ENABLED=true.
Or create it with the CLI after making an API key in Settings:
export CRONPAD_URL=http://localhost:8080
export CRONPAD_API_KEY=cp_…
cronpad jobs create --name report --schedule 'every day at 2am' --tz UTC \
--url https://example.com/your-hook
To run a command job, set CRONPAD_SINKS__COMMAND__ENABLED=true (overriding the
default Compose value), then install or
mount an executable at /usr/local/bin/backup.sh inside the container. On that
configured server, command creation is:
cronpad jobs create --name backup --schedule 'every day at 2am' --tz UTC \
--command /usr/local/bin/backup.sh
--arg adds individual arguments without shell interpolation.
On self-hosted installs, jobs can run shell commands inside the cronpad container in addition to webhooks. See Commands.
5. Check health
curl -s localhost:8080/readyz
# {"dispatcher":"active","heartbeat":"…","queue":true,"sqlite_writable":true,"writer_backpressure":false}
Next steps
- Back up your data continuously: Backups with Litestream.
- Read every setting: Configuration reference.
- Scrape metrics: Observability.
- Before you rely on it: Production checklist.