# Run on a schedule

A backup that doesn't run on a schedule is a demo. CronJob, timer, or CI.

gitdr is a one-shot job on purpose. Scheduling belongs to whatever already runs your
jobs, so there's no daemon to babysit and no extra attack surface.

## Kubernetes (Helm CronJob)

```sh
helm install gitdr oci://ghcr.io/gitdr-io/charts/gitdr \
  --set cronjob.schedule="0 2 * * *"
```

Secrets mount from Kubernetes Secrets into env, config from a ConfigMap. Prefer workload
identity (IRSA, GKE WI, Azure MI) on the pod's service account over static keys, then the
bucket credential never exists as a secret at all. See the
[chart values](https://github.com/gitdr-io/gitdr/tree/main/charts/gitdr) for the knobs.

## systemd timer

Samples in [deploy/systemd](https://github.com/gitdr-io/gitdr/tree/main/deploy/systemd).
The unit runs the binary as a locked-down service (dedicated user, env file for secrets),
the timer fires it nightly. `systemctl list-timers` shows the next run.

## Plain cron

Sample in [deploy/cron](https://github.com/gitdr-io/gitdr/tree/main/deploy/cron), a small
wrapper script that sources env and runs `gitdr backup`. Fine for a single box.

## CI pipeline

Call `gitdr backup` from a scheduled pipeline. GitHub Actions or GitLab CI schedules
work, and OIDC-based cloud auth means no long-lived bucket keys in CI secrets either.

## Alert on the one metric that matters

Set `metrics.textfilePath` and gitdr writes a `.prom` file for node_exporter's textfile
collector (atomic write, no push dependencies):

```
gitdr_last_successful_run <unix-timestamp>
```

One Prometheus rule catches every failure mode, crashed job, wedged runner, revoked
credential, full disk:

```yaml
- alert: GitBackupStale
  expr: time() - gitdr_last_successful_run > 86400 * 2
  annotations:
    summary: "git org backup hasn't succeeded in 2 days"
```

Alert on staleness, not on job failure. A job that silently never runs fires no failure
alert, but it can't fake freshness.