# The command line

`alf` (short for `smartalfred`; either name works) is the dashboard in your terminal: putting your code on Alf and deploying it, your services and computers, how they are, their logs, restarting them, and a shell inside them. For macOS, Linux and Windows.

```bash
curl -fsSL https://runalf.com/cli.sh | sh
alf login
alf launch      # in your code's directory: a project, its databases, and the first deploy
alf services
```

## Installing it

On macOS and Linux:

```bash
curl -fsSL https://runalf.com/cli.sh | sh
```

It goes in `/usr/local/bin` where you can write there, and in `~/.local/bin` otherwise — no `sudo` either way (if `~/.local/bin` isn't on your `PATH` yet, it says which line to add).

On Windows, in PowerShell or the Command Prompt:

```powershell
powershell -c "irm https://runalf.com/cli.ps1 | iex"
```

It goes in `%LOCALAPPDATA%\Programs\smartalfred`, no administrator needed, and onto your `PATH`: open a new terminal afterwards. Everything works there but [`connect --all`](#your-machine-in-a-project-s-network), which needs a network interface of its own: in [WSL](https://learn.microsoft.com/windows/wsl/install) it works (installed with the macOS and Linux line), or give the WireGuard app for Windows the file `connect --config` writes.

Either way the download's checksum is checked before anything is installed, and nothing else is changed.

Beside it goes its short name, `alf`: `alf deploy` is `smartalfred deploy`, `alf logs web` is `smartalfred logs web`. It's left out where something else is called `alf` already: then type `smartalfred` wherever these docs say `alf`. An install from before it had one gets it with `smartalfred update`.

`alf update` installs the newest version later; so does running the line again.

### Tab completion

```bash
alf completion --install     # asks, then adds it to ~/.zshrc, ~/.bashrc or fish's completions
```

Commands, their second words (`env set`, `domains add`) and their flags complete with Tab in zsh, bash, fish and PowerShell. `alf completion zsh` (or `bash`, `fish`, `powershell`) prints the script itself.

## Signing in

```bash
alf login
```

It shows a code and opens a page in your browser. Check the page shows the same code and press **Sign it in**. The page works on any device where you're signed in to Alf, so signing in a server over SSH is the same: open the link on your laptop. No password is typed in the terminal. `--no-browser` only shows the link.

`alf launch` and `alf deploy` do this themselves when the computer isn't signed in yet, and then carry on. They also see to a payment method: if your organization needs one before it deploys, they open Stripe's page and wait until it has arrived (owners and admins add it; in CI, where nobody can, they say so instead).

Every computer you sign in is listed under **Your account** in the dashboard (the menu at the top right), with where it was last used and when. **Sign out** there ends it; so does `alf logout` on the computer itself. A sign-in that isn't used for 30 days ends on its own.

The sign-in is kept in `~/.config/smartalfred/config.json`, readable only by you.

## Which organization

Commands work in one organization: the one you chose with `alf use <org>`, or your first. `alf orgs` lists yours and marks the one in use; `--org <org>` picks another for one command.

## Launching and deploying

```bash
alf launch                # this directory as a new project: files, databases, first deploy
alf deploy                # again: the code and alf.toml's settings
alf deploy web            # only one service of it
alf deploy --plan         # what it would do, and stop
alf deploy --wait         # and stay until it's live (what it does in CI)
alf deploy --preview pr-12
alf deploy --profile production   # a compose file's services in that profile too
alf config shop --save    # a project you made in the dashboard, as alf.toml here
```

[`launch`](/docs/launch) looks at the code, writes a Dockerfile where there's none, asks about the databases it uses, makes the project, writes [alf.toml](/docs/alf-toml) and deploys. `deploy` sends the directory as [`deploy.sh`](/docs/deploying-code) does — but after the first push only what changed since the last one goes up, and nothing when nothing did — (in a git repository: its files, uncommitted changes included, ignored ones left out), to the project alf.toml names (or `--project`), shows the plan, sends the code and ends with the project's link: the deployments carry on without the terminal. With `--wait`, and in CI (where `CI` is set), it follows them until they're live and exits with an error if one fails (`--no-wait` doesn't, even there). Secrets the file asks for are asked for, not shown, the first time. A setting changed in the dashboard since alf.toml set it is asked about, not undone: Enter keeps the dashboard's and writes it into the file (`--config-wins` takes the file's without asking; see [what a deploy does](/docs/alf-toml#what-a-deploy-does-with-it)). Without an alf.toml it deploys the docker-compose file or the Dockerfile, like `deploy.sh`; a compose file's services in [profiles](/docs/compose#profiles) come with `--profile`.

- **What goes up:** what a build can see, as Docker would: a `.dockerignore` leaves files out of the push too, not only out of the image. A big file that goes up anyway is named, with its size, so you can leave it out (`.gitignore` or `.dockerignore`) if the build doesn't need it. See [what's sent](/docs/deploying-code#what-it-deploys).
- **While it deploys:** a followed deploy prints the build's lines as they come, then the first lines the app printed once it's live. `--quiet` leaves them out.
- **When it doesn't start:** if the app listens on another port than the service's on its first deploy, the port is set to that one and it deploys again by itself; it says so. If the app stopped because a variable is missing, it says the `alf env set` that sets it. See [troubleshooting](/docs/troubleshooting).

## Your project at a glance

In a directory with an alf.toml, `alf` on its own (or `alf status`) shows that project: each service, how it's doing, its address, and what was deployed last and when. Elsewhere it's the help.

There, every command about a service works on that project's app without naming it: `logs`, `open`, `restart`, `stop`, `start`, `shell`, `exec`, `redeploy`, `rollback`, `env`, `run` and `domains`. When the project has several apps it asks which at a terminal, and otherwise stops and lists them; it never guesses. A name is looked up in that project first, so `alf logs web` there is its web even if another project has one too. `stop`, `start` and `restart` take several names, or `--all` for every service of the project:

```bash
alf restart               # this directory's app
alf stop web worker       # two of them (asks first)
alf stop --all --yes      # the whole project
alf exec -- npm run seed  # a command in this directory's app
```

## Deploying from GitHub

```bash
alf ci github             # in the directory with alf.toml
```

It writes `.github/workflows/smartalfred-deploy.yml` (every push to the default branch deploys the project) and `smartalfred-preview.yml` (a [preview](/docs/previews) for each pull request, ended when it closes; `--no-previews` leaves it out). Then, once you say yes, it makes a deploy token for the project, which can deploy to it and nothing else, for the repository's secret `SMARTALFRED_TOKEN`. With the [GitHub CLI](https://cli.github.com) (`gh`) installed and signed in, it saves the secret itself and the token is never shown. Without it, the token is shown once at the terminal, with the repository's page to paste it in (opened in your browser); the repository is the one the git remote points at. Commit and push the workflows, and the next push deploys. The workflows are the ones in [deploying code](/docs/deploying-code#from-ci).

## Services and computers

```bash
alf services              # every service: its state, address and server
alf service shop/web      # everything about one, and its last deployments
alf projects
alf computers             # every computer, in whichever project it is
alf computer shop/computer
```

A service is named by its name, or as `project/service` when that name is in more than one project or environment (a preview's copy doesn't count: the plain name means the project's own). A computer is named the same way, `project/computer`, by its own name when that's in one project only, or by its project's name when it's the only computer there. Ids (`svc_…`, `prj_…`) work everywhere too. Previews are left out of the lists unless you add `--all`.

## Variables, and running the code here

```bash
alf env                              # the variables, and the secrets by name
alf env set LOG_LEVEL=debug          # set, and deploy (--stage: wait for the next deploy)
alf env set --secret STRIPE_KEY      # a secret: asked for, not shown
alf env unset LOG_LEVEL
alf env push                         # a .env file's lines onto the service (asks first)
alf env pull                         # the variables into .env, for this machine
alf run -- npm run dev               # a command here, with them, the project connected
```

`env push` reads a `.env` file (comments, `export` and quotes as `.env` files have them) and sets its lines on the service: a key it has already keeps its kind, and a new one that looks like a password, key or token is offered as a secret. It shows what it will set and asks first (`--yes` doesn't).

`env set` deploys the change straight away (the code stays as it is; only the environment changes), with anything else that was [staged](/docs/variables-and-secrets) in the dashboard. A key that's a variable becomes a secret with `--secret`, and back.

`run` gives the command the service's variables the way its containers have them, and connects the private services they point at for as long as it runs: `${{db.HOST}}:${{db.PORT}}` becomes `127.0.0.1` and a port of this machine, over the project's [network](/docs/network-access) (the first time, this machine becomes one of the project's devices, as with `connect`). Anything set in your shell wins, which is how to change one for a run.

A secret's value isn't shown again once it's set — not in the dashboard, not to agents, not here — so `run` and `pull` leave secrets out and say which: set those in your shell or `.env`. Keep `.env` out of git; `pull` says so when it isn't.

## A copy of the database

```bash
alf db dump                          # its data into postgres-2026-10-20-1430.sql here
alf db restore seed.sql              # a .sql file into it (a snapshot is taken first)
```

Both run in the database's own container with its own tools (`pg_dump`, `psql`, `mysqldump`, `mysql`), so nothing needs installing here, and they move the data with flow control, so a big one doesn't pile up anywhere on its way. `dump` writes a file that loads into a database of your own (`psql -d myapp_dev -f postgres-….sql`). `restore` asks first, makes room on the database's disk for the file (a volume grows by itself, but an import can outrun it), takes a snapshot of the database so it can be undone, then loads the file. Postgres and MySQL (and MariaDB); `--db` names the database when the project has several. See [working with a database](/docs/database-access).

## Domains

```bash
alf domains add shop.example.com     # the DNS record to make, then waits until it's live
alf domains                          # each domain, and how it's doing
alf domains remove shop.example.com
```

`add` says the one record to create at the domain's DNS provider, and waits (at a terminal, or with `--wait`) while it shows up and the HTTPS certificate is issued — usually a few minutes after the record exists. See [domains](/docs/domains).

## Registries

```bash
alf registries add ghcr.io --username you   # the token is asked for, not shown
alf registries                              # the organization's logins
alf registries remove ghcr.io
```

The organization's logins for [private images](/docs/images#private-registries): services that run one, and builds whose Dockerfile starts `FROM` one. Without a terminal, the token is read from what's piped in.

## Coding agents

```bash
alf agents
```

Finds Claude Code, Codex and Cursor on this machine and, for each that hasn't got it, asks whether to add Alf's [MCP server](/docs/agents). Nothing changes without a yes (or `--yes`); with nobody at the terminal it prints the commands instead. `launch` suggests it at the end when it sees an agent that hasn't got it yet.

## Logs

```bash
alf logs web                # the last 100 lines its container printed
alf logs web -f             # …and keep following
alf logs web --since 2h --grep timeout
alf logs web --since "2026-10-14 09:00" --until "2026-10-14 10:00" --stderr
alf logs -f                 # in a launched directory: every service, interleaved
```

In a directory with an alf.toml, `logs` with no name shows all of the project's services together, in the order their lines were printed, each with its service's name, like `docker compose logs`. Name one for its lines alone.

Plain `logs` reads the running container, now. With `--since`, `--until`, `--grep` or `--stderr` it searches what Alf keeps of a service's output — the last few days, from every server it ran on; see [metrics and logs](/docs/metrics-and-logs). `-n` says how many lines.

## When something is wrong

```bash
alf doctor                  # every service of this directory's project: what's wrong, and what to do
alf errors web              # what went wrong lately: each error once, with its stack trace
alf requests web -f --status 5xx   # the requests it answers, as they come
alf top                     # each service now: CPU, memory, requests, errors
```

`doctor` puts side by side what Alf knows about a service, so the cause is in one place:

- its state, and a failed newest deploy with the failure's hint;
- running out of memory, or getting close to it;
- a volume that's full or can't grow;
- its error rate and slowness over the last hour, and slow wakes;
- domains that don't work, and variable changes waiting for a deploy;
- and, in the app's directory, variables its `.env.example` lists that the service hasn't got.

Each finding says what to run or do next. It ends with 1 when something is a problem, so a script can tell.

`errors` reads what Alf kept of its output for errors: each one once, with its stack trace, how many times and when it was first and last seen. `--since 2h` looks back less (or up to `3d`), `--trace 30` shows more of each trace.

`requests` shows each request it answered: when, the status, the method, how long it took, the path (without the query string) and the size. Its servers keep the newest few hundred, so it's what just happened; `-f` follows, `--status 5xx` (or `4xx`, `404`, `error`) and `--path /api` narrow it down, and `--with-logs` shows under each 5xx what the app printed while answering it. Calls from the project's own services are there too (`← worker`); `--from inside`, `internet` or a service's name narrows to those, and `--connections` shows the connections from inside that weren't HTTP (a database's protocol, TLS): who, the port, how long, the bytes each way and how each ended.

`top` is the project's services as they are now, refreshing at a terminal: running or asleep, CPU and memory against their limits, requests a minute, 5xx answers and the slowest responses over the last five minutes.

When a build fails, the deploy says why and what fixes it for the usual causes: a file the Dockerfile copies that wasn't sent (and which line of `.gitignore` or `.dockerignore` left it out), a lockfile out of step with `package.json`, a missing `build` or `start` script, a Node.js, Python or Go version older than a dependency needs, a base image that doesn't exist.

## A shell, or one command

```bash
alf shell shop/computer              # a shell in the computer
alf shell web                        # a shell beside the service, in a container of its own
alf shell web --live                 # in the container that serves its visitors
alf shell web -- bin/rails console   # something else, with a terminal
alf exec web -- bin/rails db:migrate # one command, then its exit code
alf exec web --live 'ls -la /app/uploads | head'
```

- A computer's shell is the computer itself. A service's is a fresh container of its image, with its variables, volumes and network — the same as the dashboard's terminal — so whatever you try there leaves the containers serving its visitors alone. `--live` opens it in the running container instead, to look at what it's doing.
- The window follows your terminal's size, Ctrl-C goes to what's running there, and leaving (`exit`, Ctrl-D) ends that shell.
- `exec` runs in the same place, with one argument through `sh -c` (so pipes work) and several as they are. It has two minutes unless `--timeout` gives it longer.
- Flags go before the command: `alf exec web --timeout 10m -- ./backfill`.
- A shell or a command is an owner's or an admin's to open, as in the dashboard.

## Your machine in a project's network

```bash
alf connect shop             # its services on localhost, until Ctrl-C
alf connect shop db=15432    # only db, on localhost:15432
alf connect shop --all       # every port, at postgres.shop.internal and the like (sudo)
alf connect shop --config    # a WireGuard file for another machine
alf disconnect shop          # revoke this machine's device
alf devices                  # every device in your projects' networks
```

The first `connect` registers this machine as a device of the project, with a key made here that never leaves it. It works for 30 days (`--days`, `--forever`), and it's listed, with whether it's connected now, in the project's settings, where it can be revoked. See [network access](/docs/network-access).

## Restarting, stopping, starting

```bash
alf restart web       # fresh containers of the version that runs now
alf restart shop/computer   # a computer: stopped, then started again
alf stop web          # asks first
alf start web
alf redeploy web      # its image again (or its code), with changed variables that wait for a deploy
alf rollback web      # back to the version that was live before (asks first)
```

`rollback` deploys the version that was live before the current one again — the same image, or the same code built again — with the variables and secrets the service has now; `--to <deployment>` goes back further (`alf service web` lists the recent ones with their ids).

`restart`, `redeploy` and `rollback` wait until the new containers are live and show what went wrong if they don't start, with the last lines the app printed. `--no-wait` returns straight away. A public service without a volume normally keeps answering while it restarts.

Restarting a computer ends whatever runs in it, so it asks first; its [workspace](/docs/computers) stays. It's also how a change that waits for a restart takes effect.

## Scheduled commands, previews and backups

```bash
alf cron                    # the project's scheduled commands: when next, how the last run went
alf cron run nightly        # run one now and see what it prints (ends with its exit code)
alf cron last nightly       # its last run, with what it printed
alf previews                # the project's previews: when each goes, and its data
alf previews open pr-12     # its address in your browser
alf previews extend pr-12 --hours 48
alf previews end pr-12      # delete it now (asks first)
alf backups                 # the project's snapshots
alf backups take db --note "before the migration"
alf backups restore snp_1a2b3c   # put one back (asks first)
```

[Scheduled commands](/docs/deployments#running-something-on-a-schedule) are made in alf.toml or the dashboard; a name may say its service (`web/nightly`). Previews are made by `alf deploy --preview <name>`; see [previews](/docs/previews). [Snapshots](/docs/backups) are taken with the container frozen for a moment, so they're safe for a database; restoring one replaces what's in the volume now, and its service is stopped meanwhile.

## Computers from the terminal

```bash
alf computer create scratch --shell                    # a new project of its own, and a shell once it runs
alf computer create build --project shop --system ubuntu --size xl --disk 50
alf computer extend shop/build --hours 48               # it goes in two days
alf computer extend shop/build --keep                   # …or not at all
alf computer create devbox --image ghcr.io/acme/devbox:latest
alf ssh shop/build                                      # SSH in, as dev (sudo works)
alf ssh shop/build -L 3000:localhost:3000               # …with a port forwarded
alf ssh-config shop/build --add                         # an SSH host for VS Code, Cursor, scp, rsync
alf cp ./site shop/build:                               # files in…
alf cp shop/build:report.pdf .                          # …and out
alf computer resize shop/build --size 2xl               # a bigger size (8 cores, 8 GB), as it runs
alf computer docker shop/build on                       # Docker in it (after a restart)
alf computer startup shop/build web -- 'npm run dev'    # started when it starts, kept running
alf computer copy shop/build                            # another like it, with what's in it
alf computer delete shop/build
```

`--system` is `claude` (Debian 12 with Node 22, which gets Claude Code when you first open it in the dashboard), `debian`, `ubuntu` or `fedora`; `--image` makes one of your own image instead. A new computer is kept until you delete it unless you say `--hours`. `--size` is xs to 3xl (m unless you say; `--memory` or `--cpus` pick the smallest size with at least so much). `--disk` is its `/workspace`'s size in GB, billed by its size. `ssh` needs OpenSSH, which macOS, Linux and Windows have; its key is the tool's own, made the first time and given to the computer each time you connect. The `sandbox` commands still work, as other names for these.

## Inside a computer

Every computer has an `alf` (and `smartalfred`) of its own for its ports, no install needed: `alf share 3000` gives a port an address, `alf ports` lists what listens; see [sharing a port](/docs/computer-access#sharing-a-port). If you install the full tool in a computer, it hands those commands to that one, so both keep working.

## What it costs

```bash
alf usage                   # this month so far, and where it's heading
```

The organization's month so far and what it comes to at this rate, its free credit or when it's charged, its spending limit, and the services the money goes to (most first, so far → the whole month). It's the same as Billing in the dashboard, where the rest is.

## Keeping it up to date

When a newer version is out, a command says so once a day, after what it printed: `alf update` gets it. It looks this up at most once a day, without holding the command up. It says nothing in CI, with `--json`, or when stderr isn't a terminal; `SMARTALFRED_NO_UPDATE_NOTICE=1` turns it off.

## In scripts

- `--json` prints what the API answered, for `jq` and friends.
- `--yes` answers the questions `stop`, `restart` and `computer delete` ask. With several services and nobody to ask which, a command stops and lists them instead of picking one.
- It ends with 0 when it worked, 1 when it didn't (and says why), 2 when the command line was wrong — and `exec` and `shell` with the command's own exit code.
- `NO_COLOR=1` turns colors off.

It signs in as you. To deploy from CI, use [`deploy.sh` with a deploy token](/docs/deploying-code) instead: it reads the same alf.toml, and `alf ci github` sets it up for GitHub Actions.

## Every command

| Command | What it does |
| --- | --- |
| `login`, `logout`, `whoami` | Sign this computer in or out; who you are |
| `launch [directory]` | This code as a new project: its files, databases and first deploy |
| `deploy [service]` | This directory's code and alf.toml, deployed |
| `config [project]` | A project as alf.toml (`--save` writes it here) |
| `ci github` | Deploy from GitHub Actions: the workflows, and the token as a repository secret |
| `agents` | Add Alf's MCP server to the coding agents here (asks first) |
| `env`, `env set`, `env unset`, `env push`, `env pull` | A service's variables: list, set and deploy, take away, from a .env file, to a .env file |
| `db dump`, `db restore <file>` | A copy of a database's data here; a .sql file into it (a snapshot first) |
| `rollback [service]` | Back to the version that was live before |
| `run [service] -- <command>` | A command here, with the service's variables and its project connected |
| `domains`, `domains add`, `domains remove` | A service's own domains; add one and wait until it's live |
| `registries`, `registries add <registry>`, `registries remove <registry>` | The organization's logins for private images |
| `orgs`, `use <org>` | Your organizations; work in another |
| `projects`, `services [project]`, `service [service]` | What you have, and everything about one |
| `projects delete <project>` | Delete a project and everything in it (you type its name first; `--yes` for scripts) |
| `computers`, `computer <computer>` | Your computers, and everything about one |
| `computer create`, `computer extend`, `computer delete` | Make, keep longer, delete |
| `ssh <computer>`, `ssh-config <computer>`, `cp <from> <to>` | SSH into a computer; it as an SSH host (`--add`); files in and out |
| `computer resize`, `computer docker`, `computer startup`, `computer copy` | Its memory and CPU as it runs; Docker in it; what it starts; another like it |
| `logs [name]` | What it printed; `-f` follows (no name: every service of the directory's project) |
| `doctor [service]` | What's wrong: why it's down, failing or slow, and what to do |
| `errors [service]` | What went wrong lately: each error once, how often, its stack trace |
| `requests [service]` | The requests it answered; `-f` follows, `--status 5xx` and `--from` narrow, `--with-logs` shows what a 5xx printed, `--connections` the connections from inside that weren't HTTP |
| `top [project]` | Each service now: CPU, memory, requests, errors |
| `cron`, `cron run <name>`, `cron last <name>` | Scheduled commands; run one now; its last run |
| `previews`, `previews open`, `previews extend`, `previews end` | A project's previews |
| `backups`, `backups take`, `backups restore <snapshot>` | Snapshots of a service's volumes; take one; put one back |
| `connect <project>`, `disconnect <project>` | This machine in a project's private network; out again |
| `devices`, `devices revoke <id>` | The devices in your projects' networks; take one off |
| `shell [name]`, `exec [name] -- <command>` | A shell in it; one command in it |
| `restart`, `stop`, `start [name ...]` | Fresh containers; switch off; switch on (several, or `--all`) |
| `move <service> <server>` | To another server, with its data (a service with volumes is stopped, copied and started there) |
| `redeploy [service]` | Deploy it again, as it is |
| `open [name]` | Its address in your browser (`--dashboard`: its page here) |
| `usage` | This month's cost so far, and where it's heading |
| `update`, `version`, `help [command]` | The newest version; which this is; more on a command |
| `completion [shell]` | Tab completion (`--install` adds it to your shell) |

`alf help <command>` has every flag.
