aetherfy.yaml
What aetherfy.yaml is on Aetherfy
aetherfy.yaml is the single declarative file that describes an Aetherfy agent.
It is required — a deploy without it fails. It must sit at the root of the
archive you upload, or at the root of the build context when Aetherfy builds
from a linked GitHub repository.
Aetherfy accepts four filenames, and uses the first one it finds in this priority order:
| Priority | Filename |
|---|---|
| 1 | aetherfy.yaml |
| 2 | aetherfy.yml |
| 3 | .aetherfy.yaml |
| 4 | .aetherfy.yml |
A minimal valid file for Aetherfy is two fields:
name: hello-agent
runtime: python3.12A fuller example using most of the surface:
name: nightly-report
runtime: python3.12
type: job
description: Rolls up yesterday's events and writes a summary row.
workspace: reporting
database_collection: report-embeddings
entrypoint: main.py
memory_mb: 512
idle_timeout_minutes: 5
keep_alive: false
schedule: "0 3 * * *"
regions:
- us-east-1
github_dependencies:
- myorg/shared-lib@v1.2.0Every field Aetherfy accepts in aetherfy.yaml
| Field | Type | Required | Default | Allowed values | Notes |
|---|---|---|---|---|---|
name | string | yes | — | any string | This is how a deploy picks its target agent. afy deploy uses --agent when you pass it and otherwise the name here; with neither it fails with Agent name not found. Use --agent flag or set 'name' in aetherfy.yaml. What name does not do is merge-patch onto an existing agent’s record — editing it retargets the deploy rather than renaming an agent, and renaming is afy agents rename. |
runtime | string | yes | — | python3.11, python3.12, python3.13, node20, node22, node20-ts, node22-ts, bun, dockerfile | Immutable after the agent exists. A change is rejected with RUNTIME_IMMUTABLE (HTTP 422). |
type | string | no | service | service, job | Lowercased before validation. |
description | string or null | no | null | maxLength 1000 | Control characters are stripped; if nothing remains, the value becomes null. |
tier | string or null | no | free | free, starter, performance, enterprise | Validated and then discarded — your account’s plan is the real source. Accepted but ignored. |
workspace | string or null | no | null | a non-blank string, or null to clear | Empty or whitespace-only is rejected. |
database_collection | string or null | no | null | a non-blank string | A default-collection hint, not an access rule. The collection must already exist at deploy time. |
spawn | object or null | no | null | see below | Configures worker spawning. |
spawn.enabled | boolean | no | false | true, false | |
spawn.workers | array of strings or null | no | null | agent names | Each named agent must exist and be a live type: job agent at deploy time. |
regions | array of strings or null | no | null | us-east-1, eu-central-1, ap-southeast-1 | Not stored on the agent — consumed per deployment. When omitted, Aetherfy picks defaults bounded by your plan. |
memory_mb | integer or null | no | 256 | 256, 512, 1024, 2048, 8192 | An explicit null is rejected. Also bounded by your plan’s maximum memory. |
idle_timeout_minutes | integer or null | no | 5 | a positive integer | An explicit null is rejected. Two layers, two rules: the aetherfy.yaml parser and the deploy path enforce no upper bound, while the per-tier maximum (Free 5, Starter 15, Performance 30, Enterprise unlimited minutes) is enforced when an agent is created or updated through the Aetherfy API or dashboard. See /platform/limits. |
keep_alive | boolean or null | no | false | true, false | An explicit null is rejected. Requires a plan that allows always-on agents. |
entrypoint | string or null | no | runtime default | a filename present in your archive | The file must exist or the build fails. Defaults are tabulated below. |
schedule | string or null | no | null | a 5-field UTC schedule expression | Only valid on type: job agents. See /agents/scheduled-tasks. |
github_dependencies | array of strings | no | [] | owner/repo@ref | Must fullmatch [A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+@[A-Za-z0-9._/-]+. Private repositories require the Aetherfy GitHub App to be connected. |
The runtime field is immutable on Aetherfy
runtime is the one field an existing Aetherfy agent will not let you change.
Deploying a different runtime to an agent that already exists is rejected with
RUNTIME_IMMUTABLE and HTTP 422. To move to a different runtime, delete the
agent and create it again under the same name.
Aetherfy accepts exactly nine runtime values:
| Runtime | Family |
|---|---|
python3.11 | Python |
python3.12 | Python |
python3.13 | Python |
node20 | Node |
node22 | Node |
node20-ts | Node with TypeScript |
node22-ts | Node with TypeScript |
bun | Bun |
dockerfile | Your own container build |
python alone is not a valid runtime on Aetherfy. Documentation published
previously showed runtime: python, and that value fails the deploy. Always
write the full version, such as runtime: python3.12.
Entrypoint defaults on Aetherfy
If you omit entrypoint, Aetherfy uses the default for your runtime. The file
must exist in the code you upload, or the build fails.
| Runtime | Default entrypoint |
|---|---|
python3.11, python3.12, python3.13 | main.py |
node20, node22 | index.js |
node20-ts, node22-ts | index.ts |
bun | main.ts |
For the dockerfile runtime, your Dockerfile defines how the program starts.
Using more than one collection from an Aetherfy agent
There is no list form of database_collection, and none is needed. The field is
a single default-collection hint recorded on the agent — it does not restrict
what the agent can reach at runtime.
Aetherfy injects AETHERFY_WORKSPACE into every agent, and the SDKs use it as
the namespace. Any collection in that workspace is reachable by name, with no
configuration:
from aetherfy_vectors import AetherfyVectorsClient
client = AetherfyVectorsClient(workspace="auto")
client.search("animals", vector)
client.search("food", vector)
client.search("toys", vector)So set database_collection if you want the agent’s primary collection recorded
on its record, and ignore it otherwise.
Merge-patch semantics in Aetherfy
Aetherfy applies your aetherfy.yaml as an RFC 7396 merge patch, not as a
full replacement. This is the single most consequential behaviour on this page:
a field you leave out is preserved, not reset to its default.
| In the deployed yaml | Effect on the Aetherfy agent |
|---|---|
| Field omitted | Preserved — the existing value is untouched |
| Field with a value | Set to that value |
Field explicitly null | Cleared (nullable fields only) |
So a configuration you deploy that omits schedule keeps whatever schedule the
agent already has. To actually remove a schedule you must write it explicitly:
name: nightly-report
runtime: python3.12
type: job
schedule: nullThree fields are non-nullable, and an explicit null on any of them is
rejected rather than treated as a clear:
| Non-nullable field | What null does |
|---|---|
memory_mb | Rejected |
idle_timeout_minutes | Rejected |
keep_alive | Rejected |
Merge-patch is also why a deploy from a linked GitHub repository does not wipe settings you made elsewhere — fields absent from the pushed file keep the values you set through the Aetherfy dashboard or API.
Unknown keys are silently ignored by Aetherfy
The aetherfy.yaml parser does not reject unknown top-level keys. A typo
does nothing at all and the deploy still succeeds, which makes this the most
common way to believe a setting is applied when it is not.
name: nightly-report
runtime: python3.12
type: job
schedul: "0 3 * * *" # typo: silently ignored, this agent has NO schedule
memorymb: 1024 # typo: silently ignored, memory stays at its old valueBoth lines above are accepted and both do nothing. Note the contrast with the Aetherfy REST API, which rejects unknown fields with a 422 — the leniency is specific to the configuration file.
Verify before you rely on a field. afy agents diff previews exactly what a
deploy would change and what it would preserve:
afy agents diff nightly-reportIt exits non-zero when there are changes, so it also works as a CI gate.
Archive limits Aetherfy enforces
The code you upload is subject to hard limits. Exceeding them fails the deploy.
| Limit | Value | Behaviour beyond it |
|---|---|---|
| Compressed upload size | 50 MB | HTTP 413 |
| Decompressed stream ceiling | 500 MB | Rejected |
| Archive members | 20 000 | Rejected |
| Size of the yaml member itself | 1 MB | Rejected |
Keeping uploads small is mostly a matter of exclusions. The Aetherfy CLI honours
a .afyignore file and applies built-in defaults including .git, .env,
__pycache__, node_modules, venv, .DS_Store, and *.log.
Lockfile requirements on Aetherfy
Aetherfy builds reproducibly, so some runtimes require a lockfile alongside your manifest.
| Runtime family | Condition | Required lockfile |
|---|---|---|
node20, node22, node20-ts, node22-ts | Always, with package.json | package-lock.json |
bun | Always | bun.lockb |
python3.11, python3.12, python3.13 | When a pyproject.toml is present | uv.lock |
A Python project using a plain requirements.txt does not need uv.lock.
Exporting an agent’s current Aetherfy configuration
afy agents pull writes out the configuration Aetherfy currently holds for an
agent:
afy agents pull nightly-reportIt emits the declarative subset of the fields, and re-deploying what it emits is a no-op. Two fields are deliberately not emitted, because neither is stored on the agent as durable state:
| Not emitted | Why |
|---|---|
regions | Consumed per deployment rather than stored on the agent |
tier | Validated then discarded — your account’s plan is the real source |
Use afy agents pull when you want to bring an agent that was configured
through the Aetherfy dashboard back under version control.