| name | unikraft |
| version | 2.0.0 |
| description | Unikraft CLI (`unikraft`) commands for building and deploying to Unikraft Cloud. Use when working with Kraftfiles, deploying unikernels, or managing Unikraft Cloud instances/services/images. Covers the new `unikraft` CLI that replaces the legacy kraftkit `kraft`. |
Unikraft CLI Reference
Build and deploy unikernels with the unikraft CLI.
Targets the new unikraft CLI 0.4.x (verified against 0.4.1) and the Unikraft Cloud REST API v1.
Run unikraft version to check; if the major or minor differs, re-verify flags/commands against --help before following this skill.
New CLI vs legacy kraftkit — they are different tools
The binary is unikraft (from unikraft-cloud/cli).
It replaces the legacy kraftkit kraft (v0.12.x).
They are separate programs with:
- a different command tree (
unikraft run / unikraft build / unikraft instances …, not kraft cloud deploy),
- a separate auth store — a
kraft login / UKC_TOKEN env from kraftkit does not authenticate unikraft; you must run unikraft login (see below).
The new CLI writes its own profile under ~/.config/unikraft, kraftkit uses ~/.config/kraftkit,
- packaging split from deploy —
unikraft build --output <ref> publishes an image, then unikraft run --image <ref> deploys it (kraftkit did both in one kraft cloud deploy).
If you see kraft, UKC_TOKEN, kraft cloud, or --rollout in a config or runbook, it is the legacy CLI — translate it to the new commands here.
Important: running commands
When working with unikraft commands:
- Show the command first — display it in a copy-paste code block before running it.
- Authenticated (cloud) commands need a login — anything hitting Unikraft Cloud (
run, instances, services, images, metros, api, a publishing build) fails with profile not setup until unikraft login has run in that environment.
The AI's shell may not be logged in; if a command returns profile not setup, hand it to the developer to run.
- Let the developer run it when a login or a real deploy is involved.
Installation
-
CI (GitHub Actions): unikraft/setup-action@v1 — installs the CLI and logs in headlessly.
Inputs: version (latest/stable, dev/staging, or a release tag), token (login token), organization.
It downloads the release asset unikraft-cli_<version>_<os>_<arch>.tar.gz from unikraft-cloud/cli.
- uses: unikraft/setup-action@v1
with:
version: 0.4.1
token: ${{ secrets.KRAFTCLOUD_TOKEN }}
organization: my-org
-
Local: unikraft upgrade self-updates an installed CLI.
Otherwise pull the tarball for your platform from https://github.com/unikraft-cloud/cli/releases (it holds the unikraft binary at its root).
-
Nix: the CLI is not in nixpkgs (only the legacy kraft is), but the Unikraft team maintains a NUR flake at github:unikraft/nur exposing packages.<system>.{unikraft-cli, unikraft-cli-staging, kraftkit} (+ an overlay).
Add it as a flake input and take unikraft-cli — or unikraft-cli-staging while the stable attr is being fixed upstream.
Authentication
The new CLI reads the token from a file or stdin, not an env var, and associates the session with an organization.
unikraft login --token /path/to/token --organization my-org
printf '%s' "$UKC_TOKEN" | unikraft login --token - --organization my-org
--organization is resolved from the control plane if omitted.
A successful login stores a profile (named after the organization) under ~/.config/unikraft.
Manage profiles with:
unikraft profile list
unikraft profile use <name>
Global flags / env available on every command: --profile ($UNIKRAFT_PROFILE), --config ($UNIKRAFT_CONFIG), --timeout ($UNIKRAFT_TIMEOUT), --log-level ($UNIKRAFT_LOG_LEVEL), --log-type, --[no-]telemetry.
Metros
Most write operations take --metro <m>.
List them with unikraft metros list:
| Use (current) | Endpoint |
|---|
fra, dal, sin, was, sfo | https://api.<metro>.unikraft.cloud |
unikraft metros list also shows legacy metros fra0, dal0, sin0, was1 on api.<metro>.kraft.cloud — these are the old kraft.cloud endpoints.
Use the suffix-less names only; treat any <metro>0 as legacy.
[!IMPORTANT]
Only the create verbs take --metro — unikraft run and unikraft services create.
The read/manage verbs (instances list/get/delete/logs, images list, services get) reject --metro with unknown flag --metro; they take a bare name (the logged-in profile spans metros) or a --filter 'metro==<m>'.
This bites in CI: piping a failed instances delete <name> --metro fra through || true silently skips the delete, and the next run then fails with "instance already exists".
Command tree
Commands
unikraft run # Run an image as an instance (deploy)
unikraft build # Build a Unikraft project into a container image
unikraft tui # Browse resources in a TUI
Resources
unikraft metros # List/inspect cloud metros
unikraft quotas # Show quota usage
unikraft instances # Manage instances (aliases: instance, vm, vms)
unikraft services # Manage service groups (svc, svcs)
unikraft volumes # Manage persistent volumes (vol, vols)
unikraft certificates # Manage TLS certificates (cert, certs)
unikraft images # Manage images (image, img, imgs)
Utilities
unikraft api <endpoint> # Authenticated raw REST call (e.g. /v1/instances)
unikraft version # Version info
unikraft upgrade # Self-update the CLI
unikraft completion # Shell completions
Config
unikraft login / logout # Authentication
unikraft profile # get / list / use
unikraft config # Manage CLI configuration
Build (package an image)
unikraft build . --output <org>/my-app:latest
unikraft build . --output ./dist/my-app.oci.tar
unikraft build . --build-arg VERSION=1.2.3 --secret id=npm,src=$HOME/.npmrc
Flags: -o/--output, --build-arg, --no-cache, --secret, --ssh, --insecure, plus the global --timeout=<duration>.
The input path defaults to ..
Run (deploy an instance)
unikraft run --metro fra --image <org>/my-app:latest \
-p 443:8080/http+tls -p 80:443/http+redirect \
-m 2048M --restart on-failure -e NODE_ENV=production
unikraft run --metro fra --image <org>/my-app:latest --dry-run
Common flags: --metro, --image, -n/--name, -e/--env, -m/--memory (binary units, e.g. 2048M), --vcpus, -p/--publish (<src>:<dest>[/<handlers>]), --domain, --service (attach to an existing service group), --restart (on-failure …), --scale-to-zero, -v/--volume, --replicas, --rm, --follow, --dry-run.
Memory units changed from legacy kraftkit: -M 2048 (MiB) → -m 2048M.
Instances, services, images
unikraft instances list
unikraft instances get <name>
unikraft instances logs <name>
unikraft instances wait <filter>
unikraft instances delete <name>
unikraft instances start|stop|suspend|restart <name>
unikraft services list
unikraft services get <name>
unikraft services create …
unikraft services edit <name>
unikraft images list
unikraft images build
unikraft images delete <ref>
volumes (create/attach/detach/clone/import/…) and certificates (create/get/wait/…) follow the same verb pattern.
[!WARNING]
Reading an instance dumps its secrets.
instances list -o json/-o yaml and instances get (without -f) return the instance's full runtime.env — including secret values such as DATABASE_URL — in cleartext.
When you only need identifiers, field-limit the output: unikraft instances list -f name -o quiet (names only, no env).
Never pipe an un-limited instance read into shared logs, CI output, or an issue.
Rolling updates (zero-downtime)
The new CLI has no single --rollout flag (kraftkit's kraft cloud deploy --rollout remove_sequential).
A zero-downtime swap of an instance behind an existing service group is composed from primitives:
unikraft run --service=<group> --image <new-ref> … — start a new instance and attach it to the service group that owns the domain/ports (do not re-pass -p/--domain; the group owns those).
Omit --name so the new instance gets a unique name and can run alongside the old one.
unikraft instances wait <filter> — wait until the new instance is ready.
unikraft instances delete <old-name> — remove the previous instance.
Confirm the exact recommended sequence (and whether a built-in rollout exists in your CLI version) with unikraft run --help / the Unikraft team before relying on it for a production deploy.
[!IMPORTANT]
Check quota headroom before a rolling update.
The overlap briefly runs two instances, so it needs ~2× the instance's memory + vCPUs at once.
Run unikraft quotas [--metro <m>] first (optional --metro, no subcommand): if adding a second instance would exceed the memory or instance cap, fall back to delete-then-recreate (accepting a brief gap) or request a quota bump.
Near the cap, a rolling run fails with a quota error mid-deploy.
Kraftfile (spec v0.7)
Keep the Kraftfile (and the Dockerfile it references) at the build context root — the rootfs path can't reach parent directories, and the Docker build needs the repo root as context to COPY sibling folders.
spec: v0.7
name: my-server
runtime: base-compat:latest
labels:
cloud.unikraft.v1.instances/scale_to_zero.policy: "off"
rootfs:
source: ./Dockerfile
format: erofs
cmd: ["/usr/local/bin/node", "/app/dist/main.mjs"]
Deployment example (build once, deploy the image)
printf '%s' "$UKC_TOKEN" | unikraft login --token - --organization my-org
unikraft build . --output my-org/my-app:2026.7.9 --timeout 10m
unikraft services create --name my-app \
--domain my-app.example.com 443:8080/http+tls 80:443/http+redirect
unikraft run --metro fra --service my-app --image my-org/my-app:2026.7.9 \
-m 2048M --restart on-failure -e NODE_ENV=production
unikraft instances list
curl -sf https://my-app.example.com/health
unikraft instances logs my-app-<suffix>
Troubleshooting
| Symptom | Cause / fix |
|---|
profile not setup | Not logged in — run unikraft login (kraftkit's login/UKC_TOKEN doesn't count). |
unknown flag --metro on */list | List/get subcommands scope by profile/--filter, not --metro. |
| Image builds but instance won't boot | Check runtime, the cmd path, and that env vars the app needs are passed with -e. |
504 Gateway Timeout | Scale-to-zero cold start — set the scale-to-zero policy off for latency-sensitive services. |
rootfs / COPY build failures | Keep the Kraftfile + Dockerfile at the build-context root; no ../ in the rootfs path. |
Legacy fra0/kraft.cloud host in a ref | Replace with the suffix-less metro (fra, api.fra.unikraft.cloud). |
Unikraft Cloud REST API (v1)
The CLI wraps a REST API.
Reach for it via unikraft api <endpoint> (uses the logged-in profile), or call it directly with a bearer token for automation.
unikraft api /v1/instances
curl https://api.fra.unikraft.cloud/v1/instances \
-H "Authorization: Bearer $UKC_TOKEN"
- Base URLs are metro-scoped:
https://api.<metro>.unikraft.cloud/v1 for fra, dal, sin, was, sfo.
Never use a <metro>0 legacy host.
- Every response uses the
{status, message, data, errors, op_time_us} envelope; bulk endpoints return per-item status.
Full endpoint tables, curl examples, and gotchas: references/api-v1.md.
Examples repository
Reference examples (Kraftfiles + Dockerfiles for many runtimes): https://github.com/unikraft-cloud/examples.