---
url: /guide/cron.md
description: Scheduled one-shot jobs with timeouts, overlap handling and manual runs.
---

# Cron jobs

A cron service is not deployed. Each trigger starts a fresh one-shot
container that runs to completion:

```toml
[services.cleanup]
type = "cron"
source = { image_of = "web" }
inherit_env = "web"
command = ["bin/cleanup", "--older-than", "30d"]
schedule = "0 2 * * *"
timeout = "15m"
```

| Key | Meaning |
|---|---|
| `schedule` | Five-field cron expression, evaluated in UTC |
| `timeout` | Maximum run time (`30s`, `5m`, `1h`). Defaults to the platform limit. |
| `command`, `env`, `resources`, `volumes`, `files` | As for any service |

Cron jobs take no `port`, `domains`, `replicas` or `health`.

`image_of` plus `inherit_env` is the common pattern: the job runs your app's
current image with the app's environment, and needs no build of its own. The
image is resolved when the run starts, so a job always runs the latest
successful build.

## Runs

* A run that is still going when the next trigger fires makes that trigger
  `skipped`; runs never overlap.
* On timeout or cancel the container gets a short grace period to exit, then
  it is stopped.
* Each run gets `DUPLOY_RUN` set to its id.

```bash
duploy run cleanup          # run now and stream the log
duploy runs --service cleanup
duploy run cancel <id>
duploy logs --run <id>
```
