Skip to Content
Agent computeScheduled tasks
Raw

Scheduled tasks

What a scheduled task is on Aetherfy

A scheduled task on Aetherfy is an agent that Aetherfy runs for you at fixed times. It is not a separate resource: there is nothing to create, register, or attach. One field in aetherfy.yaml — schedule: — gives an agent a schedule, and removing that field takes it away. The schedule is only a trigger: it does not change what kind of agent you deployed.

Each time a schedule fires, Aetherfy starts a run, and what a run does follows the agent’s type:

typeWhat each run does
jobRuns your entrypoint once, as the task contract describes
serviceSends one request to your service’s own POST /aetherfy/run route — see the run route for a service

Scheduled runs can overlap. If a run is still going when the next occurrence is due, the next one starts anyway, on a machine of its own, up to your plan’s runs-in-flight limit. A slow or stuck task can therefore have several runs executing at once. Make the task idempotent, or have it take its own lock before work that must not happen twice — see the task contract.

An overlapping run does not start instantly: its new machine has to start first, which typically takes 15–20 seconds. A run that finds a machine resting between runs starts in about a second.

A task’s run reuses the agent’s existing image, so ordinarily there is no build step and no deploy latency at fire time. If that image is no longer available, the run rebuilds it from the source Aetherfy stored for the agent’s deployed version, when that source is still stored, and then runs on the result. That one run pays for the build; the runs after it reuse the rebuilt image. A service’s run goes to the machine the service is already serving from, so it never builds anything. Runs started this way are recorded with trigger_source=cron, which is how you tell them apart from runs you started by hand. See /agents/runs-and-logs.

Declaring a scheduled task in Aetherfy configuration

Add schedule: to the agent’s aetherfy.yaml and deploy. A complete configuration for a task that runs daily at 03:00 UTC:

name: nightly-report runtime: python3.12 type: job entrypoint: main.py memory_mb: 512 schedule: "0 3 * * *"

Quote the expression. Unquoted, a YAML parser can read some expressions as something other than a string, and Aetherfy needs the literal text.

Deploy it exactly like any other Aetherfy agent:

afy deploy

The same field works on a type: service agent. Its runs are requests to a route your service serves, which is how a long-lived service does work on a cadence without a second agent to wake it:

name: catalog-api runtime: python3.12 type: service entrypoint: main.py memory_mb: 512 schedule: "0 3 * * *"

The Aetherfy schedule expression format

An Aetherfy schedule expression is a 5-field cron expression, always interpreted in UTC. It is exactly the syntax you would write for any standard cron scheduler — the format is borrowed, the feature is Aetherfy’s scheduled tasks.

┌───────────── minute │ ┌─────────── hour │ │ ┌───────── day of month │ │ │ ┌─────── month │ │ │ │ ┌───── day of week │ │ │ │ │ * * * * *
PositionFieldRange
1minute0–59
2hour0–23
3day of month1–31
4month1–12
5day of week0–6 (0 = Sunday)

One honest caveat about how Aetherfy validates this. Only the minute field has a hand-written grammar in Aetherfy. Fields 2–5 are handed to the underlying parser, which accepts a broader dialect than the table above — including 7 for Sunday and name forms for the day of week. The table is the supported core, not the outer limit of what will parse. Write expressions that fit the table and your schedule will behave as documented.

Schedule expressions Aetherfy rejects

Aetherfy validates the expression at deploy time, so an invalid schedule fails the deploy rather than silently never firing. Each rejection has its own message.

Rejected inputExampleWhy
Any @-alias@hourlyAliases are not supported — write the 5 fields
6-field syntax with seconds0 0 3 * * *Exactly 5 fields are required
An expression that can never fire0 0 30 2 *“schedule never fires (impossible date combination)” — February has no 30th
A stepped wrapping minute range50-10/15“unsupported minute-field syntax”
Anything firing more often than every 300 seconds*/2 * * * *Below the Aetherfy minimum interval — see below

The minimum interval on Aetherfy

The minimum interval between fires on Aetherfy is 5 minutes (300 seconds). An expression that would fire more often is rejected with:

schedule: fires more often than every 300 seconds (minimum interval). For continuous or sub-interval work, use an always-on agent (keep_alive) instead of a schedule.

That message names the right alternative. If you need work done continuously or faster than every five minutes, a schedule is the wrong instrument — deploy a long-running agent with keep_alive: true and control the cadence inside your own program. Always-on agents require a plan that permits them; see /platform/limits.

Why Aetherfy schedules are UTC only

Every Aetherfy schedule expression is interpreted in UTC. There is no timezone field, and this is deliberate rather than an omission.

A local-time schedule is ambiguous twice a year at daylight-saving boundaries: in the spring the named local time does not exist, and in the autumn it happens twice. Rather than pick a silent policy for those two days, Aetherfy defines schedules in a timezone that has no such boundaries.

If you want a task to land at a fixed local time, convert that time to UTC yourself and accept that it will drift by an hour across a daylight-saving change, or schedule it at a UTC hour where the drift does not matter.

Examples of Aetherfy schedule expressions

ExpressionFires
0 * * * *Every hour, on the hour
*/15 * * * *Every 15 minutes
0 3 * * *Daily at 03:00 UTC
30 2 * * 1Mondays at 02:30 UTC
15 2 * * 1-5Weekdays at 02:15 UTC

Where Aetherfy allows a schedule

Two rules govern where a schedule is valid on Aetherfy.

RuleDetail
On either agent typeA type: job agent’s run executes its entrypoint. A type: service agent’s run is a request to its POST /aetherfy/run route.
One schedule per agentTo run the same code on two cadences, deploy it as two agents with different names.

The one-schedule-per-agent rule is worth planning around. Two agents pointing at the same repository directory, differing only in name and schedule:, is the supported way to run one program hourly and also nightly.

What a scheduled run costs before your code starts

Between runs, a task’s machine is suspended — not destroyed, and not rebuilt. Triggering a run resumes it, and your entrypoint starts in the process that resume brings back.

This is the same suspend and resume an idle service agent uses, on the same machine shape. A resume is fast: it thaws a process that is already running rather than starting your machine from its image.

A scheduled run of a service agent is a request like any other the service receives. An idle service is resumed by it, exactly as it is resumed by a customer’s request, and an always-on service is already awake. Your server is not started per run, so nothing below about per-run startup applies to it.

Occasionally the suspended state does not survive: a host is taken down for maintenance, or your machine is migrated. There is nothing to resume, so the run starts your machine from its image instead. That is slower, and it is the same difference an idle service agent sees between a resume and a cold boot, which is measured on /agents/aetherfy-yaml.

Aetherfy has not yet published measured figures for a task specifically, and the service figures are deliberately not repeated here: they are measured as what a resume adds over a customer’s own request path, and a task has no such path because the platform is the only thing that triggers it. Quoting them for a task would be comparing two different measurements.

Whatever the wake costs, your own startup is on top of it, and is yours to measure. Your entrypoint is a fresh process every run, so your imports, your clients, and whatever your module does at load run on every run either way.

Two things follow for planning a cadence:

IfThen
Your task runs every few minutesThe wake is noise next to the interval, and there is nothing to tune
Your own startup is heavy — large models, big imports, warm cachesThat cost is paid on every run, and it is usually much larger than the wake. Making the process long-lived is what fixes it

If your startup is the expensive part and you need it paid once rather than per run, a task is the wrong shape for it. Deploy the work as a type: service agent that does it in its POST /aetherfy/run route, and give that agent the schedule: each run is then a request to a process that has already paid its startup. Note that keep_alive: true is rejected on type: job: a task’s machine is suspended between runs and has nothing for always-on to keep awake, so the flag would only keep the machine running and billing. Always-on is billed for the time the machine is up; see /platform/billing.

Changing or removing an Aetherfy schedule

Aetherfy applies aetherfy.yaml as a merge patch, which gives the schedule field four distinct behaviours depending on what you deploy.

What you deployWhat Aetherfy does
schedule omittedPreserves the existing schedule and does not move the next run
schedule with the same expressionLeaves the cursor alone — the next run does not shift
schedule with a new expressionRecomputes the next run from now
schedule: nullRemoves the schedule, removes its cursor, and clears any pause you had set

The last row matters if you are using pause as a temporary off switch. Deploying schedule: null does not preserve a paused state for later — it clears the pause along with the schedule, so re-adding an expression later starts a live schedule rather than a paused one.

To stop scheduled runs without editing configuration at all, pause instead:

afy schedule pause nightly-report afy schedule resume nightly-report

Pausing and resuming, overlap behaviour, and missed windows are covered on /agents/managing.

Building a schedule in the Aetherfy dashboard

You do not have to write the expression by hand. The Aetherfy configurator at https://app.aetherfy.com/dashboard/agents/configurator  builds a valid expression from presets and previews the upcoming runs in both UTC and your local time, which is the fastest way to confirm that a UTC expression lands where you expect it to locally. The builder is offered for either agent type.

PageWhat it covers
/agents/task-contractHow your code runs and exits, how it reads input, the at-most-once guarantee, and the run route a service answers
/agents/managingRunning on demand, pausing and resuming, overlaps, missed windows
/agents/runs-and-logsRun history, run states, and log retrieval
/agents/aetherfy-yamlEvery configuration field and the merge-patch rules
Last updated on