Compute
Run a Compute Function automatically on a recurring schedule.
Some jobs run on a clock, not on a request — refreshing a table of exchange rates, recomputing a daily rollup, warming a cache, syncing an external source. These are ordinary Python jobs, but they need a stable environment with the right libraries and credentials, and something reliable to trigger them.
A scheduled function is a Compute Function that Chalk
invokes for you on a recurring schedule. You write the function once, declare a
schedule in the decorator, and deploy it — Chalk runs it on the platform at the
cadence you specify, with the custom image, resources, secrets, and retry
behavior you configured. There is no separate cron server to operate.
Bake your dependencies into an image so each run starts ready to go. Anything your job imports — data clients, model libraries — belongs here:
import chalkcompute
image = chalkcompute.Image.debian_slim().run_commands(
"pip install requests",
)Add the schedule argument to the @chalkcompute.function decorator. A
scheduled function takes no arguments — it is invoked by the platform, so there
is no caller to pass inputs. Everything it needs, it fetches or computes itself:
import chalkcompute
@chalkcompute.function(
name="refresh-exchange-rates",
schedule="0 * * * *", # every hour, on the hour
image=image,
secrets=[chalkcompute.Secret.from_env("EXCHANGE_API_KEY")],
cpu="1",
memory="1Gi",
retries=3,
)
def refresh_exchange_rates() -> None:
import os
import requests
resp = requests.get(
"https://api.example.com/v1/rates",
params={"base": "USD"},
headers={"Authorization": f"Bearer {os.environ['EXCHANGE_API_KEY']}"},
)
rates = resp.json()["rates"]
# write the latest rates to your store
...The custom image, cpu / memory requests, and secrets configure the
Scaling Group that hosts the function; schedule
and retries govern how and when the platform invokes it.
Scheduled jobs often do heavier work — nightly batch inference, periodic re-scoring of a dataset. Request a GPU the same way you would for any function:
@chalkcompute.function(
name="nightly-batch-inference",
schedule="0 3 * * *", # daily at 03:00 UTC
image=image,
gpu="nvidia-l4",
cpu="4",
memory="16Gi",
)
def nightly_batch_inference() -> None:
...Deploying registers the function and its schedule with the platform. Run the script:
uv run python refresh_exchange_rates.pyFrom this point on, Chalk invokes refresh_exchange_rates on its schedule. No
further action is required to keep it running.
The schedule argument accepts either a crontab expression or a Chalk duration
string. Use whichever reads more clearly for your cadence. All schedules are
evaluated in UTC.
Crontab — five space-separated fields (minute hour day-of-month month
day-of-week). Standard syntax is supported: *, lists (1,15), ranges
(1-5), and steps (*/10).
| Expression | Runs |
|---|---|
"*/10 * * * *" | every 10 minutes |
"0 * * * *" | every hour, on the hour |
"0 2 * * *" | every day at 02:00 UTC |
"0 0 * * 0" | every Sunday at 00:00 UTC |
Duration shorthand — a single interval, for simple cadences. Supported units are minutes (≤ 60), hours (≤ 24), and days (= 1), written as one value:
| Shorthand | Runs | Equivalent crontab |
|---|---|---|
"10m" | every 10 minutes | */10 * * * * |
"6h" | every 6 hours | 0 */6 * * * |
"1d" | every day | 0 0 * * * |
Shorthand intervals are aligned to clock boundaries, not to your deployment time:
"6h" runs at 00:00, 06:00, 12:00, and 18:00 UTC. Anything longer or compound
("90m", "1h30m", "2d") must be written as a crontab expression.
@chalkcompute.function(schedule="0 2 * * *") # nightly at 02:00 UTC
def nightly_rollup() -> None:
...
@chalkcompute.function(schedule="15m") # every 15 minutes
def warm_cache() -> None:
...Because a scheduled function is invoked by the platform rather than a caller, two constraints apply and are enforced when the function is defined:
yield. Return None (or a value) instead.Everything else about a Compute Function still applies — custom images, resource requests, secrets, and retries all work exactly as they do for on-demand functions.
Each invocation appears in the Chalk dashboard with its status, logs, and resource usage, just like a manually invoked function. Emit logs from the body to make each run observable:
from chalk import chalk_logger
@chalkcompute.function(name="refresh-exchange-rates", schedule="1h")
def refresh_exchange_rates() -> None:
chalk_logger.info("refresh started")
rates = ... # fetch the latest rates and write them to your store
chalk_logger.info("refreshed %s rates", len(rates))