---
id: platform/cron-jobs
title: Cron jobs
description: Schedule run-to-completion commands, inspect run history, and trigger or cancel a Bex cron run.
keywords: [bex, cron job, schedule, cron expression, run history]
last_updated: 2026-09-23
---

Cron jobs run commands on a schedule and exit when the work is complete.
Use them for reports, periodic cleanup, and other bounded tasks. They expose
no HTTP endpoint. Use a [background worker](./background-workers.md) for a
continuous consumer or a process that should remain running.

## Deploy a scheduled command

The repository's [cron demo](https://github.com/bex-co/bex/tree/main/examples/cron-demo)
prints a message every five minutes:

```yaml
services:
  - name: cron-demo
    type: cron
    runtime: docker
    repo: https://github.com/bex-co/bex
    rootDir: examples/cron-demo
    branch: main
    schedule: "*/5 * * * *"
    plan: free
    envVars:
      - key: MESSAGE
        value: "hello from the Bex cron job"
```

Follow the [Blueprint guide](./app-resource.md) to authenticate, validate, and
deploy the file. Docker cron jobs run the image's default command; for a
Dockerfile build, `dockerCommand` can override it. Native-runtime jobs need
build and start commands appropriate to the selected runtime.

Use a standard five-field cron expression: minute, hour, day of month, month,
and day of week. Bex currently passes the expression to Kubernetes without
setting a per-job timezone. The Kubernetes controller's timezone determines
wall-clock scheduling; confirm it with your instance operator before relying
on a particular local hour. The every-five-minutes example avoids that ambiguity.

## Inspect runs separately from deployments

A deployment prepares the executable and schedule. A run executes the command.
An App phase of `Running` means the cron schedule is configured, not that the
last execution succeeded. Open the dashboard's run history to inspect each
run's status and logs.

For API access, set `BEX_API_URL` to your API origin, `BEX_TOKEN` to an
authorized Bearer token, and `SERVICE_ID` to the cron service's id:

```bash
curl --fail-with-body "$BEX_API_URL/v1/cron-jobs/$SERVICE_ID/runs" \
  -H "Authorization: Bearer $BEX_TOKEN"
```

## Trigger or cancel a run

A manual trigger can interrupt current work: Bex requests cancellation of an
active run before creating the replacement. To trigger:

```bash
curl --fail-with-body -X POST \
  "$BEX_API_URL/v1/cron-jobs/$SERVICE_ID/runs" \
  -H "Authorization: Bearer $BEX_TOKEN"
```

The response represents the requested run; cancellation and startup happen
asynchronously. Follow run history and [logs](./logging.md) for the outcome.
To cancel the currently active run:

```bash
curl --fail-with-body -X DELETE \
  "$BEX_API_URL/v1/cron-jobs/$SERVICE_ID/runs" \
  -H "Authorization: Bearer $BEX_TOKEN"
```

Cancellation does not undo database writes or external side effects already
performed. Make jobs safe to retry and protect operations that must not run twice.

## Scheduling and execution limits

- Scheduled runs use Kubernetes `Forbid` concurrency policy. Overlapping
  scheduled executions are skipped rather than launched concurrently. The
  operator also suspends the schedule while a manual run is active.
- Suspending the service pauses scheduling; use cancellation to stop an active
  run. Resume restores scheduling. Do not rely on suspension as an exact
  replay queue for missed work.
- Bex's current cron controller does not set a 12-hour job deadline. Set a
  timeout in your application when tasks must finish within a fixed duration.
- Cron jobs do not expose a listener or have HTTP readiness checks. Use run
  results and logs to detect failure.

These are Bex implementation details. [Render's cron documentation](https://render.com/docs/cronjobs)
describes its own scheduling and maximum-duration behavior; do not assume
those limits transfer unchanged.

## Troubleshoot a missing or failed run

Check that the deployment completed, the schedule is valid, the service is not
suspended, and another execution is not still active. For failed executions,
inspect the command's exit status and logs, credentials, dependency reachability,
and whether the task exceeds its available resources.

See [App lifecycle](./app-lifecycle.md) for suspend/resume and
[Secrets](./secrets.md) for database and API credentials.

For an explicitly requested standalone command rather than a recurring schedule,
see [one-off jobs](./one-off-jobs.md) and their execution-environment limits.
