Deploy an agent to Aetherfy
This tutorial takes you from an empty directory to a deployed Aetherfy agent
that answers HTTP requests, then shows you its logs. It uses the afy CLI
throughout and asks you to make no design decisions along the way.
You need the afy CLI installed before you start. Installation instructions are
on /cli.
Get an Aetherfy API key
Create an API key at
https://app.aetherfy.com/dashboard/settings/api-keys .
Keys look like afy_live_xxxxx. Copy it when it is shown.
The Aetherfy CLI reads your key from its stored login, and it also honours the
AETHERFY_API_KEY environment variable if one is set.
Sign in to Aetherfy with afy login
Run afy login and paste the key when prompted:
afy loginFor CI or any non-interactive shell, pass the key directly:
afy login --api-key afy_live_xxxxxConfirm the CLI is talking to your Aetherfy account:
afy whoamiCreate the agent’s files for Aetherfy
Make a directory and add three files. This example is a service agent — a
long-lived HTTP server — written with the Python standard library only, so
there is nothing to install and nothing that can break during the build.
mkdir hello-agent
cd hello-agentaetherfy.yaml — the configuration Aetherfy reads. It must sit at the root of
the directory you deploy:
name: hello-agent
runtime: python3.12
type: service
memory_mb: 256Write the runtime version in full. runtime: python is not valid on
Aetherfy and fails the deploy; the Python values Aetherfy accepts are
python3.11, python3.12, and python3.13.
main.py — the entrypoint. main.py is the default entrypoint for every
python3.* runtime on Aetherfy, so you do not need to declare it. A service
agent must listen on port 8080, and Aetherfy health-checks it with
GET /health on that port:
"""A minimal Aetherfy service agent: answers HTTP requests on port 8080."""
import json
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
PORT = 8080
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/health":
self._respond({"status": "ok"})
return
self._respond(
{
"message": "hello from Aetherfy",
"agent": os.environ.get("AETHERFY_AGENT_NAME", "unknown"),
"region": os.environ.get("AETHERFY_REGION", "unknown"),
}
)
def _respond(self, body):
payload = json.dumps(body).encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(payload)))
self.end_headers()
self.wfile.write(payload)
def log_message(self, fmt, *args):
# Anything printed to stdout becomes this agent's logs on Aetherfy.
print(fmt % args, flush=True)
if __name__ == "__main__":
print(f"hello-agent listening on 0.0.0.0:{PORT}", flush=True)
HTTPServer(("0.0.0.0", PORT), Handler).serve_forever()requirements.txt — present but empty, because this example imports only the
standard library. Aetherfy installs from it if it lists anything:
# No third-party dependencies: this agent uses the Python standard library only.Letting afy init write the configuration instead
afy init inspects a directory, detects the project type, and generates
aetherfy.yaml for you. Every prompt it asks has a matching flag — --name,
--runtime, --entrypoint, --type, --region, --memory, --keep-alive,
--workspace, and --schedule — and -y / --yes accepts all defaults, which
makes it usable in CI:
afy init --name hello-agent --runtime python3.12 --type service --yes-f / --force overwrites an existing aetherfy.yaml. Note that -y does not
imply --force, so afy init -y will not clobber a file you already have.
Deploy the agent to Aetherfy
From inside the directory, run:
afy deployafy deploy uploads the current directory, builds an image, launches machines,
waits for completion, and streams status as it goes. It prompts once to confirm
cost before it starts.
The name: field in aetherfy.yaml is what this deploy targets. With no
--agent flag, Aetherfy’s CLI reads name from the file — hello-agent here —
and that is the agent the deploy goes to, and the name the agent is created
under on this first run. If you pass neither, the command stops with
Agent name not found. Use --agent flag or set 'name' in aetherfy.yaml.
Changing name later retargets the deploy at a different agent rather than
renaming this one; renaming is afy agents rename.
Useful flags on the Aetherfy deploy command:
| Flag | Effect |
|---|---|
--detach / -d | Return as soon as the upload finishes instead of waiting |
--agent / -a | Deploy to a named agent, overriding the name in aetherfy.yaml |
--yes / -y | Skip the cost confirmation prompt |
Aetherfy does not upload everything in the directory. Files matching
.afyignore are excluded, along with built-in defaults including .git,
.env, __pycache__, node_modules, venv, .DS_Store, and *.log.
Confirm the Aetherfy agent is running
When the deploy finishes, the deployment reaches the active state and the
agent’s status becomes running. Check it from the CLI:
afy agents listYou can see the same thing in the dashboard at https://app.aetherfy.com/dashboard/agents .
If the deploy failed instead, the deployment ends in the failed state and the
error is recorded against it. List the history with afy deployments hello-agent
to read the failure, and see /agents/runs-and-logs for
what each state means.
Read the Aetherfy agent’s logs
Everything your program writes to stdout and stderr becomes the agent’s logs on
Aetherfy. The startup line from main.py above appears here:
afy logs hello-agentFollow the logs as they arrive:
afy logs hello-agent --followShow more history, or narrow to a time window and a severity:
afy logs hello-agent --tail 200 --since 1h --level ERROR,WARNAetherfy retains logs for 7 days. The full flag list, the per-line and volume caps, and the dropped-line marker are documented on /agents/runs-and-logs.
Preview a change before deploying it to Aetherfy
Aetherfy applies aetherfy.yaml as a merge patch, so fields you leave out keep
their existing values rather than resetting. Before a deploy you are unsure
about, preview exactly what would change and what would be preserved:
afy agents diff hello-agentafy agents diff exits non-zero when there are changes, which makes it usable
as a CI gate that fails a pipeline on unintended configuration drift.
What to do next with Aetherfy
| Next step | Page |
|---|---|
| Every configuration field and the merge-patch rules | /agents/aetherfy-yaml |
| Turn this into a run-once task on a schedule | /agents/scheduled-tasks |
| How a task reads input, exits, and is retried (it is not) | /agents/task-contract |
| Give the agent API keys and other secrets | /agents/secrets |
| Deploy automatically on every push | /agents/github |
| The rest of the Aetherfy CLI surface | /cli/agents |