# 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](../self-hosting/production-checklist.md) 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

```sh
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):

```sh
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`:

```sh
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](../self-hosting/authentication.md).

Create a persistent encryption key before saving jobs, including with development
login, and tell Compose to pass `.env` into the server:

```sh
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](../secrets/index.md) for rotation.

## 3. Start it

```sh
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](../self-hosting/authentication.md#first-sign-in) to let others in.

:::note
cronpad refuses to start if no sign-in method is configured, so a misconfigured server is never left open.
:::

## 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:

```sh
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:

```sh
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](../sinks/commands.md).

## 5. Check health

```sh
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](../self-hosting/backups.md).
- Read every setting: [Configuration reference](../self-hosting/configuration.md).
- Scrape metrics: [Observability](../observability/index.md).
- Before you rely on it: [Production checklist](../self-hosting/production-checklist.md).