Command line
keal is the engine from a terminal: the same API the web UI uses, as the
account you sign in with. It is one file with nothing to install beside it, for
Linux, macOS and Windows.
Installing
Before there is an engine, install the latest release from the registry:
curl -fsSL https://registry.keal.sh/downloads/install.sh | sh
It checks the release's signature as well as its checksum, where there is an
OpenSSL that can check one. From there, keal engine install builds an engine
(see Building an engine).
On Linux or macOS, the engine serves an installer for the CLI that goes with it:
curl -fsSL https://your-engine/static/cli/install.sh | sh
The installer works out your system and processor, fetches the matching build
from the engine it came from, checks it against the engine's checksums, and puts keal in
/usr/local/bin - or in ~/.local/bin if you may not write there. To choose
the place yourself:
curl -fsSL https://your-engine/static/cli/install.sh | KEAL_INSTALL_DIR="$HOME/.local/bin" sh
If the engine has a self-signed certificate, or one from an authority your
machine does not trust yet, add -k to the first curl and set
KEAL_INSECURE=1 so the installer's own downloads skip the check too. The build
is still checked against its checksum. Only do this on a network you trust:
curl -k -fsSL https://your-engine/static/cli/install.sh | KEAL_INSECURE=1 sh
About, in the web UI's user menu, has the command ready to copy with this engine's address already in it.
Windows, or downloading by hand
There is no installer for Windows: download the archive, unpack it, and put the
program somewhere on your PATH. Each archive can be checked against
checksums.txt.
| Platform | Download |
|---|---|
| Windows x64 | keal_windows_amd64.zip |
| Windows ARM64 | keal_windows_arm64.zip |
| macOS Apple silicon (macOS 13 and later) | keal_darwin_arm64.tar.gz |
| macOS Intel (macOS 10.15 and later) | keal_darwin_amd64.tar.gz |
| Linux x64 | keal_linux_amd64.tar.gz |
| Linux ARM64 | keal_linux_arm64.tar.gz |
On Windows, unzip keal_windows_amd64.zip, put keal.exe somewhere on your
PATH (or run it from its folder), and use it from PowerShell or Command
Prompt - keal login, keal vms ls.
The first run on Windows takes about 20 seconds
keal.exe is not code-signed, so the first time it runs Windows Defender
scans it - and where a company network blocks Defender's online check, it
holds the program back for about 20 seconds waiting for an answer. It happens
once for each new copy of the program.
To get it over with, double-click keal.exe once in Explorer. If Windows
asks whether to send the file to Defender, say yes; if SmartScreen says
"Windows protected your PC", choose More info, then Run anyway. A
window opens with the help text and closes again - that is expected. From then
on keal starts at once. Starting it from a terminal never brings up that
question, so double-click every new copy once - including one keal update
has just installed.
On a Mac, a build downloaded with a browser is marked as coming from the internet, and macOS will not run a program that is not signed by an identified developer. Unpack it, then lift the mark before running it the first time:
tar -xzf keal_darwin_arm64.tar.gz
xattr -d com.apple.quarantine keal
sudo mv keal /usr/local/bin/
The installer above fetches with curl, which does not mark anything, so this
is only needed for a download by hand.
Updating
Once it is installed, run keal update. It compares your version with the
one the engine offers and does nothing if you have it already; otherwise it
fetches the build for your system, checks it against its checksum and replaces
the program where it is. keal update --check only looks; --force installs it
again regardless. keal version says which version you have, and keal about
which version the engine has too.
The update comes from the engine you are signed in to, so the CLI stays in step
with it. With no engine, or one that offers no CLI, it comes from the latest
published release instead, whose signature is checked as well.
keal update --latest takes the latest release even when the engine offers
another one. When a release newer than the engine's is published,
keal update says so, but does not install it unless you add --latest.
Where you may not write
keal update replaces the program file itself. If it is in a system place
such as /usr/local/bin, run it with sudo.
Uninstalling
keal uninstall
removes the program and its settings - the engines it knows, the sessions signed
in to them, and the history of its sessions - after asking. -y answers yes;
--keep-settings removes only the program, so that installing it again finds
everything as it was.
A program in a system folder such as /usr/local/bin needs the rights to remove
it: the settings go, and keal tells you what to run with sudo to finish. On
Windows a running program cannot delete itself, so there keal removes the
settings and tells you which file to delete.
Removing keal does not end a session on the engine any sooner: it lasts until it
runs out. If one may have been copied somewhere, change your password.
Signing in
keal login
The installer has already told keal which engine it came from. If you
downloaded it by hand - or it should talk to another engine - say which, once:
keal login --url https://your-engine.
You are asked for your username and password, and for your two-factor code if your account has one. The session lasts as long as one in a browser, and is renewed as you work. It is kept in keal's settings, which only you can read:
| System | Settings |
|---|---|
| Linux | ~/.config/keal/config.json |
| macOS | ~/Library/Application Support/keal/config.json |
| Windows | %AppData%\keal\config.json |
KEAL_CONFIG_DIR keeps them somewhere else, and keal about says where they are. keal whoami says who
you are and where, when you signed in and when the session runs out; keal
logout forgets the session.
Where there is no one to ask — a script, a scheduled job — the same comes from
the environment: KEAL_URL, KEAL_USER, KEAL_PASSWORD, KEAL_TOTP.
A self-signed certificate
An engine whose certificate your machine does not trust is refused. If that
is expected, add --insecure (or set KEAL_INSECURE=1).
Finding your way
Each kind of thing has its own group of commands, and they all work alike:
keal vms ls |
List them. keal vms ls web narrows to what contains web; keal vms ls 'web-*' to what matches as a whole. |
keal vms get NAME |
Show one in full. |
keal vms start NAME... |
Tell one or more to do something. |
keal vms set NAME notes="rebuilt in May" |
Change settings. KEY= with nothing after it clears one. |
keal vms rm NAME... |
Remove. |
The groups are vms, stacks, images, networks, nodes, users and
monitors; keal --help lists the rest. keal vms --help lists what a VM can be told to do, and
keal vms set --help what can be set.
Anything can be named by its name, its id, or the start of its id (four characters or more). A name that two things share is refused, with the ids to choose between.
Press Tab to complete commands and names, once your shell has been told how:
keal completion bash --help (and likewise zsh, fish, powershell). A name
is shown with the start of its ID beside it, web-01 (1f2b69c0). Where two share
a name, each completes as the name with the start of its ID after it -
Ubuntu Noble (1f2b69c0) - which every command takes as meaning that one.
After an action, Tab offers only what it applies to as things stand: keal vms
start offers the VMs that are off, keal vms shutdown those that are running,
each with what it is doing beside it.
Waiting for it to finish
Most of what the engine is told to do it does in its own time, and the command
returns as soon as the work has started. Add -f (--follow) to stay until it
is done:
keal vms start web-01 -f
keal images clone base-disk -f
keal stacks build "Ubuntu Noble" --name shop --start -f
It prints each change of status, and what the engine reports on the way, until
the thing has settled or is gone. Ctrl-C stops following, not the work;
--timeout says how long to stay at most (30 minutes unless told).
A session
keal -i (or keal interactive) starts a session: commands are typed without
the keal in front, Tab completes commands and names without the shell having
been set up for it, and history is kept from one session to the next.
$ keal -i
keal> vms ls
keal> stacks get shop
keal> exit
Ctrl-C stops what is running and comes back to the prompt. What is given on
keal's own command line — --context, -o json — holds for the whole
session. A terminal on a node needs the command line to itself, so nodes ssh
and nodes console are run outside a session.
Being asked first
A command that removes or interrupts something asks before it does. -y answers
yes. Where there is no terminal to ask at, a command that would have asked stops
instead, so a script has to say -y to mean it.
For scripts
Add -o json to a listing or a get for the records as the engine gives them.
Piped or redirected, a table is plain aligned columns with no colour.
keal api reaches anything in the engine's API that has no command of its own:
keal api get /nodes/stats
keal api post /images/IMAGE-ID/convert -d '{"type": "qcow2"}'
A path is under the API, so /servers means /api/v1/servers. The API tab of
the web UI lists what there is.
More than one engine
A context is an engine's address with the session signed in to it:
keal context add lab https://lab.example
keal login
keal context ls
keal context use default
--context NAME uses another for a single command.
Acting for another account
If your account has been given others to act for, keal account ls lists them
and keal account use ACCOUNT switches; keal account reset goes back.
--as ACCOUNT does it for one command.
Making things
Each of these is the web UI's dialog for the same thing, with the same defaults,
so a bare create is enough wherever pressing Create alone would be:
keal networks create web-net --type mapped --ports 80,443
keal images create scratch --size 50
keal vms create web-01 --image web-01-disk --network web-net --memory 4096 --start
keal stacks create shop --vm web-01 --vm db-01
A VM boots from one of your images that nothing else is using. It goes on
--network NAME, or on a network made for it with --new-network — private,
mapped (private, with a public address mapped to it), public, or remote (a
public address at the registry, tunnelled here, on an engine that offers it).
To build an application in one step, pick it from the catalogue:
keal stacks catalog # --tag linux for those tagged so
keal stacks build "Ubuntu Noble" --name shop --instances 2 --start
keal stacks describe APPLICATION says what one is and what it is built with;
the build uses those settings wherever a flag does not say otherwise. An
application marked to be fetched is brought to the engine with
keal stacks fetch APPLICATION (administrators).
keal stacks open NAME opens a stack's or a VM's own management page in your
browser, through the engine; --page terminal or --page upgrade opens those,
and --print only says the address.
Moving VMs between stacks
keal vms move web-01 db-01 --stack shop
keal vms move web-01 --out
Nothing about the VMs themselves changes. keal stacks rm takes a stack apart
and leaves its VMs standing; keal stacks destroy-all removes the VMs, images
and networks with it.
A terminal on a node
For administrators:
keal nodes ssh NODE
keal nodes console NODE
ssh opens a shell on the node, through the engine — nothing has to be
reachable from where you are but the engine. console opens the node's serial
console through its management controller (AMT or IPMI), which works whatever
the node itself is doing. Both are the same sessions as the
Terminal and Serial console buttons.
Leave with exit, or with ~. at the start of a line when there is nothing
listening to exit (~~ sends a ~).
How things stand
keal status is the web UI's Home in a terminal: how many stacks,
VMs, images and networks you have, and a word on the health of your VMs, the
checks kept on them, the updates they are waiting for and their backups - and,
for an administrator, the engine's nodes.
keal status
keal status -o json
keal status --check || echo "something needs looking at"
--check makes the command fail when something is wrong - a VM has crashed, a
check is failing that nobody has acknowledged, or a node is not answering - so a
script or a scheduled job can act on it. Waiting updates, missing backups and
things shut down on purpose do not count.
What the web UI would warn you of on opening - missing backups - keal says in
a line when you sign in and when a session starts. It is written
to the terminal only, never into a pipe or a file, and not with -o json.
Usage, software and metrics
Wherever a command is about "a stack or a VM", either can be named, the same way as anything else.
keal usage # what you use now, against your quotas
keal usage --month 2026-09 # the average over a month
keal usage --of shop # one stack or VM
keal usage --csv > usage.csv # each month of the year so far
keal software os # your VMs' operating systems and waiting updates
keal software ls --updates # what has an update waiting
keal software scan shop # read what is installed, a stack or a VM at a time
keal software update shop --security
keal software schedule shop --every daily --security
keal software os --csv > systems.csv
keal metrics web-01 # now, average and peak over the last 20 minutes
keal metrics shop --range 24h # a stack's VMs added together
keal metrics engine # the engine itself (administrators)
Monitors
keal monitors ls
keal monitors add web-01 --service https --request /health --expect ok
keal monitors set MONITOR-ID port=8443
keal monitors events --of shop
keal monitors uptime --month 2026-09
keal monitors uptime --csv > uptime.csv
keal monitors detail MONITOR-ID shows the last check step by step — how long
each part took, what came back, when a certificate runs out — and with
--headers makes a web check again and shows the answer's headers.
keal monitors services lists the kinds of check there are. A monitor has no
name of its own unless you give it one, so it is named by its id, or the start
of it.
Images in and out
keal images upload ./debian-12.qcow2
keal images fetch https://example.org/images/debian-12.qcow2
keal images download web-01-disk --to /backups/
keal images backups web-01-disk
keal images restore web-01-disk --backup BACKUP
keal images set web-01-disk size=80 pool=1
upload sends a file from your machine; fetch has the engine download one
itself, from an address it can reach. download never overwrites a file that is
already there.
Accounts and your own settings
keal settings show
keal settings set alertemail=ops@example.org phone=
keal passwd
keal users create ada --name "Ada Lovelace" --email ada@example.org
keal users set ada privileges=m storagequota=500000 # quotas: not on a linked engine
keal users passwd ada
A VM's or a stack's own contacts, services and autostart are set like its other
settings: keal vms set web-01 services="web, mail" autostart=1.
The engine
keal engine status # is it up, and is its own work running
keal engine settings # how long a session lasts
keal nodes stats # the nodes' capacity, added up
keal events # what is happening, as it happens
keal events shows what the web UI's notifications are made from, for as long
as it runs; -o json gives one event a line.
For administrators, the engine's own settings and its registry:
keal engine show
keal engine set vmreadlimit=400 downloadmasters=1
keal engine link --account you@example.org
keal engine link-status
keal engine unlink
engine link asks for the account's password, and for its two-factor code when
the account has two-factor sign-in. On a linked engine, a setting the registry
sets is refused by engine set, and so are the accounts' quotas in users set:
they are changed at the registry.
The engine's own backups, kept at the registry (see Backups):
keal engine backups # on or off, and those kept
keal engine backups key # choose a passphrase; turns them on
keal engine backups key --no-passphrase # or let the registry keep the key
keal engine backups now
keal engine backups off # and on
keal engine backups restore 2026-10-20.tar.gz.enc --check
keal engine backups --engine <old engine ID> # another engine's, to restore here
restore --check opens a backup and changes nothing; without --check it puts
it back. The passphrase is asked for when one is needed, or read from
KEAL_BACKUP_PASSPHRASE.
Updating the engine
keal engine update --check # which Keal it runs, and the latest release
keal engine update # update it to the latest (administrators)
keal engine update --version v0.8.0
The engine updates itself and restarts as it does, and keal engine update
waits until it is back (--wait 0 does not wait). It is what the Update
button in the web UI's About dialog does (see
Updating).
Building an engine
keal engine install installs Keal on a fresh Ubuntu 24.04 server, over ssh,
or on this machine when no host is given. It runs the release's own installer
(see Install an engine):
keal engine install --check root@192.0.2.10 # check the server; changes nothing
keal engine install root@192.0.2.10 --hostname keal.example.com
keal engine install ubuntu@server --upload # for a server that cannot reach the registry
keal engine install ubuntu@server --release ./keal-v0.7.0.tar.gz
keal engine uninstall root@192.0.2.10 # take it off again
- ssh: it uses your own
ssh, so your ssh config, agent, keys and jump hosts all apply. The account needs sudo on the server, and sudo can ask for its password. - The release: it installs the release matching this
keal, or the one--versionnames. By default the server fetches it from the registry.--uploadfetches and checks it here and sends it over ssh.--releasesends a tarball you have, checked against theSHA256SUMSandSHA256SUMS.sigbeside it if they are there. - Afterwards:
kealhas a context for the new engine, named by--nameor the host, signed in as its first administrator. The context trusts the engine's self-signed certificate by that certificate itself, so it works whatever address the engine is reached by, NAT included. The administrator's password is shown once, for the web UI, and is not kept anywhere. - The installer's options:
--admin,--hostnameand--node-nicare handed to it, and so are the registry's:--registry-user,--dns-name,--dns-address,--no-link,--no-dns-nameand--no-registry, and--nginxor--no-apachefor the web server in front,--port, and--listen 0.0.0.0to reach Keal straight for a test (see A name and a certificate). The installer's questions, the registry password included, come through ssh.
keal engine uninstall takes --keep-data, --remove-packages and -y, as
the installer's --uninstall does, and asks first unless given -y.
The audit log
keal audit # who changed what, the latest 50
keal audit --since 7d --what servers --failed
keal audit clear --older-than 90d # administrators, where Engine settings allow it
See Audit for what is recorded and who sees it.
What is not there
Setting up storage devices, and a node's GPUs and resource limits, are still
only in the web UI or within reach of keal api. A VM's graphical console
needs a browser and stays in the web UI.