> ## Documentation Index
> Fetch the complete documentation index at: https://docs.specific.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Crons

> Scheduled jobs with tracked runs, logs, and metrics.

A cron is a scheduled job: like a service, but instead of running continuously it runs a command on a recurring schedule. Each execution is a tracked **run** with its own logs and metrics.

```hcl specific.hcl theme={null}
build "worker" {
  base    = "node"
  command = "npm run build"
}

cron "daily-cleanup" {
  build    = build.worker
  command  = "npm run cleanup"
  schedule = "0 2 * * *"        # 02:00 UTC every day

  env = {
    DATABASE_URL = postgres.main.url
  }
}

postgres "main" {}
```

A cron is shaped like a service: it has its own `build` (or `image`), `command`, `env`, and `root`. To share code between a long-running service and a cron, point both at the same `build` block.

## Configuration

* `schedule` (required) - when to run. Either a standard 5-field cron expression (`minute hour day-of-month month day-of-week`, interpreted in **UTC**) or a macro: `@hourly`, `@daily`, `@weekly`, `@monthly`, `@yearly`.
* `build` - reference to a `build` block. Mutually exclusive with `image`.
* `image` - a pre-existing container image. Mutually exclusive with `build`.
* `command` - the command to run on each tick. Required when using `build`.
* `root` - working directory for the command, relative to `specific.hcl`. Defaults to the referenced build's `root`.
* `env` - environment variables, using the same references as services (`postgres.*`, `secret.*`, `config.*`, `service.<name>.private_url`, interpolated strings, and so on).
* `dev` - a `{ command, env }` block to override the command and env when running the cron locally.

Crons don't serve network traffic, so they cannot declare `endpoint`, `volume`, or `health_check` blocks.

### Schedule examples

```hcl theme={null}
schedule = "0 2 * * *"    # 02:00 every day
schedule = "*/15 * * * *" # every 15 minutes
schedule = "0 9 * * 1"    # 09:00 every Monday
schedule = "@hourly"      # top of every hour
schedule = "@daily"       # midnight every day
```

### Image-based crons

A cron can run a pre-existing image instead of a build:

```hcl specific.hcl theme={null}
cron "hourly-sync" {
  image    = "python:3.12"
  command  = "python /app/sync.py"
  schedule = "@hourly"
}
```

## Local development

Crons do **not** fire on their schedule during `specific dev`, since that would be surprising while iterating. Instead, trigger a cron manually, exactly like a one-off command:

```bash theme={null}
specific exec daily-cleanup            # runs the cron's own command
specific exec daily-cleanup -- npm run cleanup:dry-run   # run a different command
```

The command runs with the cron's resolved environment variables and working directory, with databases and other resources started, the same way `specific exec <service>` works. Crons are also listed in the [local dashboard](/concepts/dashboard#the-local-dashboard) (`specific dev` prints its URL) under **Crons**.

## Production

When deployed, each cron runs on its schedule in the cloud. Every execution is a tracked run:

* Runs are visible in the [dashboard](https://dashboard.specific.dev) under **Crons → (cron) → runs**.
* Each run has its own logs and CPU/memory metrics, collected the same way as services.
* Overlapping runs are allowed - if a run is still going when the next is due, the next one starts anyway.
* You can trigger a run on demand with the **Run now** button.
