---
slug: cli/deploy
title: afy init, deploy, deployments, and rollback in the Aetherfy CLI
kind: reference
surface: cli
summary: Reference for the Aetherfy CLI deployment commands — afy init generates aetherfy.yaml, afy deploy uploads and builds, afy deployments lists version history, and afy rollback re-deploys a previous version by bare integer.
sources:
  - aetherfy-cli:cmd/init.go
  - aetherfy-cli:cmd/deploy.go
  - aetherfy-cli:cmd/deployments.go
  - aetherfy-cli:cmd/rollback.go
  - aetherfy-cli:internal/archive/tarball.go
  - aetherfy-cli:internal/detect/detect.go
---

# afy init, deploy, deployments, and rollback

## The Aetherfy deployment commands

Four Aetherfy CLI commands cover the path from an empty directory to a running
agent and back again.

| Command | Purpose |
|---|---|
| `afy init [path]` | Scan a directory and generate `aetherfy.yaml` |
| `afy deploy [path]` | Upload the code, build an image, and deploy it |
| `afy deployments <agent>` | List the agent's deployment history |
| `afy rollback <agent> [version]` | Re-deploy a previously built version |

A first deployment on Aetherfy is normally two commands:

```bash
afy init
afy deploy
```

## Generating an Aetherfy manifest with afy init

`afy init [path]` scans a directory, detects the runtime and entrypoint, and
writes an `aetherfy.yaml` manifest. The path argument is optional and defaults to
`.`.

| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
| `--name` | | string | empty | Agent name (skips prompt) |
| `--runtime` | | string | empty | Runtime: python3.11, python3.12, python3.13, node20, node22, node20-ts, node22-ts, bun, dockerfile (skips prompt) |
| `--entrypoint` | | string | empty | Entrypoint file, e.g. main.py or index.js (skips prompt) |
| `--type` | | string | empty | Agent type: service or job (skips prompt) |
| `--region` | | string | empty | Region: us-east-1, eu-central-1, ap-southeast-1 (skips prompt) |
| `--memory` | | int | 0 | Memory in MB: 256, 512, 1024 (skips prompt) |
| `--keep-alive` | | bool | false | Enable always-on billing (skips billing prompt) |
| `--workspace` | | bool | false | Enable VectorDB workspace (skips workspace prompt) |
| `--force` | `-f` | bool | false | Overwrite existing aetherfy.yaml without asking |
| `--yes` | `-y` | bool | false | Accept every prompt's default (non-interactive) |
| `--schedule` | | string | empty | Cron schedule for job agents (UTC, 5-field, min every 5 minutes), e.g. `'0 3 * * *'` |

Interactive by default; any flag above skips its prompt.

```bash
# Interactive
afy init

# Fully specified, non-interactive
afy init ./services/scraper \
  --name catalogue-scraper \
  --runtime python3.12 \
  --entrypoint main.py \
  --type job \
  --region eu-central-1 \
  --memory 512 \
  --schedule '0 3 * * *' \
  --yes
```

Behaviour of the two boolean shortcuts in the Aetherfy CLI:

| Flag | Effect |
|---|---|
| `--yes` / `-y` | Accepts every prompt's default. It does **not** imply `--force`. |
| `--force` / `-f` | Overwrites an existing `aetherfy.yaml`. Required even alongside `-y`. |

The non-interactive defaults Aetherfy applies under `--yes`:

| Setting | Default |
|---|---|
| Name | The directory name |
| Type | `service` |
| Region | `us-east-1` |
| Memory | 256 MB |
| Always-on billing | Off |
| Runtime | **Detected** — `afy init --yes` fails if the runtime cannot be detected |

The schedule prompt appears only for `job` type in interactive mode. The
resulting `schedule:` key is what makes the agent a scheduled task; see
[/agents/scheduled-tasks](/agents/scheduled-tasks) and the full manifest
reference at [/agents/aetherfy-yaml](/agents/aetherfy-yaml).

## Deploying to Aetherfy with afy deploy

`afy deploy [path]` validates the manifest, archives the directory, uploads it to
Aetherfy, builds an image, and deploys it. The path argument is optional and
defaults to `.`.

| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
| `--detach` | `-d` | bool | false | Return immediately after upload without waiting for completion |
| `--agent` | `-a` | string | empty | Agent ID or name (reads from aetherfy.yaml if not specified) |
| `--from-github` | | string | empty | Deploy from a public GitHub repo: owner/repo[@ref] |
| `--yes` | `-y` | bool | false | Skip the overage confirmation prompt and proceed (non-interactive) |

```bash
# Deploy the current directory
afy deploy

# Deploy a subdirectory, non-interactively, in CI
afy deploy ./services/scraper --yes

# Upload and return immediately
afy deploy --detach

# Target an agent explicitly
afy deploy --agent catalogue-scraper
```

The order of operations is: validate `aetherfy.yaml`, archive the project
directory as a gzipped tarball, upload, build the image, deploy.

`afy deploy` is one of the Aetherfy commands that exits 1 on failure, so it is
safe to rely on its status in a pipeline.

## Files excluded from an Aetherfy deploy archive

The archive respects a `.afyignore` file in the project directory. On top of
whatever it contains, the Aetherfy CLI always excludes these built-in defaults:

| Pattern |
|---|
| `.git` |
| `.gitignore` |
| `.env` |
| `.env.*` |
| `__pycache__` |
| `*.pyc` |
| `*.pyo` |
| `.pytest_cache` |
| `.mypy_cache` |
| `node_modules` |
| `.npm` |
| `venv` |
| `.venv` |
| `env` |
| `.DS_Store` |
| `Thumbs.db` |
| `*.log` |
| `.afyignore` |

Because `.env` and `.env.*` are excluded unconditionally, environment values must
reach the agent as Aetherfy secrets rather than as files — see
[/cli/secrets](/cli/secrets).

An example `.afyignore`:

```
tests/
fixtures/
*.ipynb
docs/
```

## Deploying to Aetherfy directly from a public GitHub repository

`--from-github owner/repo[@ref]` deploys from a public GitHub repository without
a local clone.

```bash
afy deploy --from-github aetherfy/examples
afy deploy --from-github aetherfy/examples@v1.2.0
afy deploy --from-github aetherfy/examples@8f2c1b0d9e4a7c3f5b1d6e8a0c2f4b6d8e0a2c4f
```

| Detail | Value |
|---|---|
| Ref default | `main` |
| Accepted refs | Branch, tag, or a full 40-character commit SHA |
| Local requirement | `git` must be installed locally |
| Repository visibility | Public only |

For private repositories and push-triggered deploys, use the Aetherfy GitHub App
instead — see [/cli/github](/cli/github).

## Aetherfy plan overage confirmation on deploy

If a deployment would push your account's usage beyond the plan allowance, the
Aetherfy CLI asks for confirmation before proceeding.

- `--yes` accepts the prompt and proceeds non-interactively.
- Without `--yes` in a non-interactive shell the command fails and tells you to
  re-run with `--yes`.

CI pipelines that deploy to Aetherfy should therefore always pass `--yes`:

```bash
export AETHERFY_API_KEY=afy_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
afy deploy --yes
```

Plan allowances are documented at [/platform/limits](/platform/limits).

Aetherfy runs one deployment per agent at a time. Starting a second while the
first is still building or deploying returns HTTP 409 `DEPLOYMENT_IN_PROGRESS`.
This is worth handling in CI, where two pushes in quick succession are the
normal way to hit it: wait for the first to reach a terminal state, or cancel it
with `afy agents cancel <agent>` before starting another.

## Partially successful multi-region Aetherfy deployments

A deployment that succeeds in some regions and not others is reported by the
Aetherfy CLI as:

```
Deployment is serving but DEGRADED — N/M regions ready.
```

The agent is serving traffic from the regions that came up. Check
`afy agents status <name>` for the per-region detail, and `afy logs <name>` for
the failure reason in the regions that did not.

## Listing Aetherfy deployment history

`afy deployments <agent>` lists the agent's deployments, newest first. It takes
one required argument and has no flags of its own; it honours the global
`-o json`.

```bash
afy deployments catalogue-scraper
afy deployments catalogue-scraper -o json
```

| Column | Meaning |
|---|---|
| Version | The version number — the value you pass to `afy rollback` |
| State | Deployment state, with a glyph |
| Created | When the deployment was created |
| Error | Failure reason, when there is one |

State glyphs used by the Aetherfy CLI:

| Glyph | State |
|---|---|
| `●` | active |
| `✗` | failed |
| `○` | superseded |
| `·` | queued |
| `⟳` | building |
| `⟳` | deploying |
| `↩` | rolled_back |

When the newest deployment failed, the Aetherfy CLI prints a suggested rollback
command underneath the table.

## Rolling back an Aetherfy deployment

`afy rollback <agent> [version]` re-deploys a previously built version.

| Flag | Short | Type | Default | Description |
|---|---|---|---|---|
| `--detach` | `-d` | bool | false | Return immediately without waiting for completion |

Called with only the agent name, it prints the deployment history so you can
choose a version:

```bash
afy rollback catalogue-scraper
```

Called with a version, it rolls back to it:

```bash
afy rollback catalogue-scraper 3
```

The version is a **bare integer**. `afy rollback catalogue-scraper v3` is
rejected by the Aetherfy CLI with `Version must be a positive integer`.

| Property | Behaviour |
|---|---|
| Build step | Skipped — the target version's already-built image is re-deployed directly |
| Valid targets | Only successfully-built versions, i.e. those in `active` or `superseded` state |
| Invalid targets | `failed`, `queued`, `building`, and `deploying` versions have no usable image |

Because the build is skipped, a rollback on Aetherfy is substantially faster than
a fresh `afy deploy` of the same code.
