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.

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:

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 -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, which needs a network interface of its own: in WSL 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

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

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

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 looks at the code, writes a Dockerfile where there's none, asks about the databases it uses, makes the project, writes alf.toml and deploys. deploy sends the directory as deploy.sh 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). Without an alf.toml it deploys the docker-compose file or the Dockerfile, like deploy.sh; a compose file's services in profiles come with --profile.

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:

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

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 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 (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.

Services and computers

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

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 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 (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

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.

Domains

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.

Registries

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: 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

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. 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

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. -n says how many lines.

When something is wrong

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:

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

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'

Your machine in a project's network

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.

Restarting, stopping, starting

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 stays. It's also how a change that waits for a restart takes effect.

Scheduled commands, previews and backups

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 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. Snapshots 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

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. If you install the full tool in a computer, it hands those commands to that one, so both keep working.

What it costs

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

It signs in as you. To deploy from CI, use deploy.sh with a deploy token instead: it reads the same alf.toml, and alf ci github sets it up for GitHub Actions.

Every command

CommandWhat it does
login, logout, whoamiSign 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 githubDeploy from GitHub Actions: the workflows, and the token as a repository secret
agentsAdd Alf's MCP server to the coding agents here (asks first)
env, env set, env unset, env push, env pullA 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 removeA 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 deleteMake, 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 copyIts 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 endA 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)
usageThis 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.