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

# Cron

> Add one argument to a function and the platform calls it on a schedule. No scheduler to run, nothing to drift.

A deployed function can carry a schedule. The platform calls it, so there is no
worker of yours to keep alive.

```python heartbeat.py theme={null}
import cycls

reports = cycls.Volume("daily-reports")


@cycls.function(
    schedule=cycls.Cron("0 3 * * *", timezone="Asia/Riyadh"),
    volumes={"/reports": reports},
)
def nightly():
    from datetime import datetime, timezone
    now = datetime.now(timezone.utc)
    with open(f"/reports/{now:%Y-%m-%d}.log", "a") as f:
        f.write(f"{now.isoformat()} ran\n")
```

```bash theme={null}
cycls deploy heartbeat.py
#   [DEPLOYING] Scheduled: 0 3 * * * (Asia/Riyadh)
#   [DONE] Deployment complete!
```

Pair a schedule with a [volume](/build/volumes) so the output has somewhere
durable to land. A scraper feeding a dashboard is the usual shape.

## Writing the schedule

```python theme={null}
cycls.Cron("0 3 * * *")                          # daily at 03:00 UTC
cycls.Cron("*/15 * * * *")                       # every fifteen minutes
cycls.Cron("0 6 * * 1", timezone="Asia/Riyadh")  # Mondays at 06:00 Riyadh
```

Five-field unix cron with IANA timezone names, UTC by default. Both are validated
at deploy time, so a typo is an error rather than a schedule that never fires.

## Source is the truth

The schedule exists because the line exists. Delete `schedule=` and redeploy and
the schedule is gone, which the deploy output confirms. `cycls rm` removes it with
the deployment. There is no pause button and no separate object to keep in sync.

## What can be scheduled

Bare functions only, the kind that deploy as named endpoints. Two cases fail at
import with the fix in the message:

* an app or agent with `schedule=`, since they serve HTTP. Schedule a function
  that calls them instead.
* a function that takes `port`, since the port contract deploys a server and a
  schedule cannot fire one.

## Semantics

<AccordionGroup>
  <Accordion title="Runs are at least once">
    Failed runs are retried, so write idempotent output. Date-keyed files like the
    example above make a double fire a harmless overwrite.
  </Accordion>

  <Accordion title="Runs can overlap">
    A run slower than its interval does not block the next one. If two runs must
    never race, take a lock file on the volume.
  </Accordion>

  <Accordion title="Thirty minutes per run">
    A single scheduled run has a thirty minute ceiling. Split longer work into slices,
    or have the scheduled function fan out with `.map()`.
  </Accordion>

  <Accordion title="Every fire is a request">
    `cycls logs <name>` shows each one, with the same structured logging as any other
    call.
  </Accordion>
</AccordionGroup>

## Next

<Card title="Apps" icon="server" href="/build/apps">
  Serve a FastAPI application with auth and per-user storage.
</Card>
