From ai-standards
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`.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ai-standards:unikraftThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Build and deploy unikernels with the `unikraft` CLI.
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.
The binary is unikraft (from unikraft-cloud/cli).
It replaces the legacy kraftkit kraft (v0.12.x).
They are separate programs with:
unikraft run / unikraft build / unikraft instances …, not kraft cloud deploy),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,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.
When working with unikraft commands:
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.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.
The new CLI reads the token from a file or stdin, not an env var, and associates the session with an organization.
# From a file:
unikraft login --token /path/to/token --organization my-org
# From stdin (no token on disk / no token in argv):
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 # profiles + their metros + which is active
unikraft profile use <name> # switch active profile
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.
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 runandunikraft services create. The read/manage verbs (instances list/get/delete/logs,images list,services get) reject--metrowithunknown flag --metro; they take a bare name (the logged-in profile spans metros) or a--filter 'metro==<m>'. This bites in CI: piping a failedinstances delete <name> --metro frathrough|| truesilently skips the delete, and the nextrunthen fails with "instance already exists".
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 the project in the current directory and PUBLISH to the registry.
# The <org> prefix is your organization (see `unikraft profile list`).
unikraft build . --output <org>/my-app:latest
# Build and save to a LOCAL OCI archive (no publish):
unikraft build . --output ./dist/my-app.oci.tar
# Build-time inputs and secrets:
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 ..
# Deploy a new instance exposing an HTTPS service:
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
# Preview without deploying:
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.
unikraft instances list # or: ls
unikraft instances get <name> # inspect
unikraft instances logs <name> # console output
unikraft instances wait <filter> # block until instances match (readiness gate)
unikraft instances delete <name> # remove
unikraft instances start|stop|suspend|restart <name>
unikraft services list # service groups (load balancers)
unikraft services get <name>
unikraft services create … # create a named service group
unikraft services edit <name> # change its config / instance set
unikraft images list # image refs + digests (--filter 'ref~="my-app"')
unikraft images build # same engine as `unikraft 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 yamlandinstances get(without-f) return the instance's fullruntime.env— including secret values such asDATABASE_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.
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 rollingrunfails with a quota error mid-deploy.
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
# base-compat runs the node binary bundled in the image.
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"]
# 1) Log in (once per environment).
printf '%s' "$UKC_TOKEN" | unikraft login --token - --organization my-org
# 2) Build + publish a versioned image.
unikraft build . --output my-org/my-app:2026.7.9 --timeout 10m
# 3) First deploy — create the service group, then run into it.
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
# 4) Subsequent deploys — see "Rolling updates" above.
# Verify:
unikraft instances list
curl -sf https://my-app.example.com/health
unikraft instances logs my-app-<suffix>
| 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). |
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 # via the CLI (logged-in profile)
curl https://api.fra.unikraft.cloud/v1/instances \
-H "Authorization: Bearer $UKC_TOKEN" # raw, any HTTP client
https://api.<metro>.unikraft.cloud/v1 for fra, dal, sin, was, sfo.
Never use a <metro>0 legacy host.{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.
Reference examples (Kraftfiles + Dockerfiles for many runtimes): https://github.com/unikraft-cloud/examples.
npx claudepluginhub guillempuche/ai-standards --plugin ai-standardsGuides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Enforces test-driven development: write failing test first, then minimal code to pass. Use when implementing features or bugfixes.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.
2plugins reuse this skill
First indexed Jul 10, 2026