k7 -h for built-in help. Below are the primary commands.
Talking to the K7 API
By default, every sandbox-management command (create, list, delete,
pause, resume, fork, restore, exec, logs, snapshot *) talks
to the K7 API over HTTPS. The CLI resolves the URL, key, and CA in this
order:
--api-url/--api-key/--api-caflags (global, before the command).K7_API_URL/K7_API_KEY/K7_API_CAenvironment variables.~/.config/k7/config.toml— written byk7 config set api.url .../k7 config set api.key .../k7 config set api.ca ....- On a cluster node only:
/etc/k7/api_endpointand/etc/k7/tls/ca.crt. The API key is never stored in the clear on disk — paste the tokenk7 generate-api-keyprinted intok7 config set api.key.
https:// URL is always verified: there is no --insecure. If the
cluster was installed with --api-hostname (Let’s Encrypt) the system
trust store is enough and api.ca is not needed; with --api-insecure-http
use an http:// URL.
--core (debugging / on-node CI only)
A hidden global flag --core skips the API and calls K7Core directly
in-process. Useful when the API itself is misbehaving and you have
kubeconfig access on the node. Most users should never need it.
config
~/.config/k7/config.toml with mode 0600. Same posture as
~/.kube/config and ~/.docker/config.json. api.ca is the path of the
CA bundle to trust for the https:// API URL (the cluster CA from
/etc/k7/tls/ca.crt on the default install).
install
Install K7 components on host node(s).- -v: verbose output
- —backend, -b: required. Comma-separated backends:
kata-firecracker-devmapper(kfd),kata-qemu-longhorn(kql),k7d,k7d-fc. Omit is an error.k7d-fcadds jailed Firecracker (installs pinned Firecracker + jailer, RuntimeClassk7-fc). k7 0.4.0 vendors Firecracker v1.16.2. - —disk: block device for the kfd LVM thin-pool (auto-detected when omitted). On dual-NVMe boxes leave this unset — NVMe names swap across reboots.
- —k7d-version: k7d release to download (k7d backends only). Playbook default is
0.7.0. Override with--k7d-artifactfor a local tarball. - —k7d-artifact: local k7d release tarball to install instead of downloading (k7d backends only).
- -i, —inventory: Ansible inventory for multi-node installs (per-host
k7_backendsis authoritative — don’t combine with--backend). - —ha: multi-master HA with embedded etcd (inventory with 3+ servers).
- —role / —join / —join-token: add a single node to an existing cluster.
- —cni:
cilium(default; FQDN egress, platform isolation,--api-allow-cidr) orflannel(CIDR-only egress). - —hubble: opt-in Cilium Hubble flow observability (relay + pinned
hubbleCLI, no UI). Requires Cilium. Not a security control. - —api-allow-cidr CIDR: repeatable. Restrict the
k7-apiNodePort to these source CIDRs via aCiliumNetworkPolicyandexternalTrafficPolicy: Local. Off by default; Cilium only;0.0.0.0/0warns. - —api-hostname NAME: DNS name pointing at the first master; Caddy obtains a Let’s Encrypt certificate (HTTP-01 on
:80). A bare IP is refused. - —api-tls-cert / —api-tls-key: operator-supplied server certificate + key (PEM). Both required together; incompatible with
--api-hostname. - —api-insecure-http: serve
k7-apias a plain HTTP NodePort (no Caddy sidecar). Keys travel in cleartext. Cannot be combined with the hostname / cert flags. - —no-api: CLI-only install without the k7-api Deployment.
version
Check version of installed K7 .deb packagecreate
Create a sandbox from a YAML file or flags.pause
On kql, scale a sandbox to 0 replicas, optionally taking a crash-consistentVolumeSnapshot of its root PVC (longhorn VolumeSnapshotClass) — and of the
<name>-docker-lh graph PVC when the sandbox was created with --docker. On
k7d / k7d-fc, pause freezes the live VM in place (vCPUs stop, memory
stays) — no snapshot. --snapshot is kata-qemu-longhorn-only.
--snapshot(optional, takes optional value): when present, take aVolumeSnapshotof the sandbox’s root PVC (<sandbox>-root-lh) before scaling down. Bare flag → snapshot named<sandbox>-paused-<unix-ts>; with value → snapshot named as given.- Pause-time snapshots persist as
VolumeSnapshotobjects in the same namespace until you delete them.
resume
restore
Boot a brand-new sandbox from a standaloneVolumeSnapshot. Unlike k7 fork,
this does not require the original sandbox’s Deployment to still exist —
it only needs the snapshot.
k7.io/source-* annotations stamped on the snapshot
at creation time (pause / named only — fork-temp snapshots are auto-deleted).
Any flag above overrides the corresponding annotation. --no-keep-snapshot
deletes the source snapshot after the new sandbox is Ready. Restore is
kata-qemu-longhorn-only.
fork
Clone an existing sandbox. Behavior depends on the backend:k7d/k7d-fc— warm CoW fork of disk and memory (~5 ms at the VMM, ~2–4 s end-to-end to a Ready pod). The child inherits files, tmpfs, and running processes — including a live--dockerengine and its inner containers. Lands on the source’s node. Sandboxes with a real CRI sidecar cannot be forked.kql/kata-qemu-longhorn— disk-only clone via a crash-consistent VolumeSnapshot of<sandbox>-root-lh(and<sandbox>-docker-lhwith--docker), then a cold boot (~45 s single-node; longer with Longhorn replicas ≥ 2).kfd— rejected.
--expose-port Service) and is rolled back if the policies cannot be created.
--snapshot(optional): friendly name for the temporary VolumeSnapshot. If omitted, an auto-named<source>-fork-<unix>snapshot is created and auto-deleted once the new sandbox’s PVC is bound.- Pass
--snapshot=NAMEto opt the snapshot out of GC (it becomes anamedsnapshot — persistent).
snapshot
ManageVolumeSnapshot objects directly. See /k7/api/endpoints/snapshots for the lifetime rules and the three kinds (pause, fork, named).
gc only ever touches kind=fork snapshots older than --keep-fork-for. Pause and named snapshots are never collected, even with --keep-fork-for=0s. A CronJob (k7-snapshot-gc in kube-system) runs the same logic every 10 minutes as a backstop, in case the inline cleanup after k7 fork ever misses a snapshot. GC also reaps orphan VolumeSnapshotContent objects whose VolumeSnapshot is gone or deleting and clears the PVC source-protection finalizer, so a deleted namespace no longer sticks in Terminating on volumesnapshotcontent-bound-protection.
YAML configuration reference
All fields map to the server-sideSandboxConfig:
- name (string, required): unique sandbox name.
- image (string, required): container image, e.g.
alpine:latest. Registry host must be public and on the allowlist (registry-1.docker.io,ghcr.io,quay.io,public.ecr.aws; extend withK7_REGISTRY_ALLOWLIST). - backend (string, optional):
kata-firecracker-devmapper/kfd,kata-qemu-longhorn/kql,k7d, ork7d-fc. - docker (boolean, default
false): run a pinneddockerdinside the sandbox withoverlay2on a block graph disk — see Docker in a sandbox. - docker_disk (string, default
20Gi): graph disk size fordocker: true. - sidecar (string, optional): deprecated;
dockeris an alias ofdocker: true. - namespace (string, default
default): Kubernetes namespace. - env_file (string, optional): (absolute) path to an env file on the host node.
- egress_whitelist (array of CIDR or FQDN strings, optional): omit /
null= open egress;[]= block-all; listed entries = allowlist. FQDNs need Cilium (the default CNI). CLIk7 createwithout--egress/--egress-openis block-all. - ingress_ports (int[], optional): TCP ports to open for inbound traffic. Omit /
[]= deny all ingress (default). - ingress_from (string[], optional): who may reach
ingress_ports—sandbox:<name>,namespace:<ns>,cidr:<cidr>. Omitted with ports set = sandboxes in the same namespace. Requiresingress_ports. - expose_ports (int[], optional): publish these TCP ports outside the cluster via a
NodePortService; each must also be iningress_ports. - volumes (object[], optional): hostPath / PVC entries. Refused on
k7d-fc(Firecracker has no virtiofs). - limits (object, optional): resource limits:
- cpu (string): cores or millicores, e.g.
"1"or"500m". - memory (string): e.g.
"1Gi","512Mi". - ephemeral-storage (string): e.g.
"2Gi".
- cpu (string): cores or millicores, e.g.
- before_script (string, optional): shell script run once after container starts.
- Runs via
kubectl execbefore egress netpol is applied; readiness waits for completion.
- Runs via
- pod_non_root (boolean, optional): run Pod as non-root (UID/GID/FSGroup 65532).
- container_non_root (boolean, optional): run container as non-root (UID 65532), no privilege escalation.
- cap_add (string[], optional): add back Linux capabilities (default policy drops ALL).
- cap_drop (string[], optional): override drop policy. If omitted,
ALLis dropped by default.
k7.yaml:
list
--expose-port (empty when the sandbox is not exposed). The table truncates names; k7 exec NAME still needs the full name you passed to create.
delete
delete-all
shell
kubectl exec); it does not go through the K7 API. From a laptop use
k7 exec NAME -- 'echo hello' — the CLI joins the command and the API
runs it under sh -c, so pass redirects as one quoted string
(k7 exec NAME -- 'echo hi > /tmp/state.txt'). Do not add an extra sh -c
or the quotes are lost.
exec
logs
sandbox container). Empty output with exit 0 means that container has not written anything — it is not a failure. k7 exec output does not appear here (exec goes through the agent / Kubernetes API). On k7d / k7d-fc, a sleep/alpine sandbox that you have only exec’d into commonly has empty logs; before_script on Kata backends is more likely to show up.
--follow is --core only (node-local kubectl logs -f).
top
nodes storage
k7-agent DaemonSet on every node:
kata_thinpool— the kfd LVM thin-pool (lvssize, data%, metadata%).k7d_disks— the k7d XFS disk pool at/var/lib/k7d/disks(dfsize / used / avail).
error entry — nodes are never silently omitted. Also available as GET /api/v1/nodes/storage (API reference) and Client.nodes_storage() in the SDK. This is cluster-wide topology, so a namespace-scoped API key gets 403.
api
The K7 API is deployed automatically byk7 install and runs as a Kubernetes
Deployment (k7-api in kube-system). K3s keeps it running and reschedules
it on failure — there is no separate “start” step. The k7 api sub-app
provides read-only diagnostics and a feature toggle:
k7-api pod. The cluster CA lives at /etc/k7/tls/ca.crt on the node — copy it to clients (k7 config set api.ca). k7 install --api-hostname (Let’s Encrypt), --api-tls-cert/--api-tls-key, and --api-insecure-http change what is served; see install.
To install without the API (CLI-only deployment, e.g. for local-only
test rigs), pass --no-api to k7 install. Re-run k7 install later
(without --no-api) to add it; the playbook is idempotent.
k7 start-api / k7 stop-api /
k7 api-status / k7 get-api-endpoint are deprecated; they still work
for one release and print a one-line warning pointing at the new home.
API keys
/etc/k7/api_keys.json. Use with X-API-Key or Authorization: Bearer.
-n / --namespace (repeatable) scopes the key to one or more namespaces. A scoped key cannot read or mutate another namespace and cannot perform all-namespaces list/GC. Omit -n for unrestricted access (backward compatible). k7 list-api-keys shows the scope (* = unrestricted).
Flag reference (create)
- -n, —namespace: Kubernetes namespace (default
default). - -f, —file: YAML config file (defaults to
k7.yamlin the current directory when present). - name / image (positional):
k7 create NAME IMAGE. There is no--name/--imageflag. - -b, —backend:
kfd/kql/k7d/k7d-fc(aliask7-fc, or the long names). - —docker: Run a pinned
dockerd(overlay2 on a block graph disk). k7d / k7d-fc: guest agent service, forkable. Kata (kfd / kql): privilegeddocker-vehiclein the VM; kql persists the graph, kfd is ephemeral. - —docker-disk: Docker graph disk size for
--docker(e.g.20Gi,40Gi). Default20Gion all backends. - —sidecar: Deprecated.
--sidecar dockeris an alias of--docker. - —cpu: CPU limit (e.g.,
1,500m). - —memory: Memory limit (e.g.,
1Gi,512Mi). - —storage: Ephemeral storage limit (e.g.,
2Gi). - —root-disk-size: Longhorn root PVC size (
kqlonly; default10Gi). - —env-file: Path to env file on the host node injected as a Secret.
- —egress ENTRY: Repeatable; whitelist CIDR or FQDN. Default (no flag): block all.
- —egress-open: Allow all egress. Mutually exclusive with
--egress. - —ingress-port PORT: Repeatable; TCP port to open for inbound traffic. Default (no flag): deny all ingress.
- —ingress-from SOURCE: Repeatable;
sandbox:<name>|namespace:<ns>|cidr:<cidr>. Default when--ingress-portis given: sandboxes in the same namespace. Reaching the sandbox from anywhere needscidr:0.0.0.0/0, spelled out. Requires--ingress-port. - —expose-port PORT: Repeatable; publish a TCP port outside the cluster through a
NodePortService (externalTrafficPolicy: Local). Requires a matching--ingress-port. On a public node the node IP is public too. - —before-script: Shell script to run once after container start; runs with open egress before lockdown.
- —entrypoint: Repeatable; override image ENTRYPOINT (ordered).
- —cmd: Repeatable; override image CMD (ordered).
- —pod-non-root / —no-pod-non-root: Pod-level non-root defaults.
- —container-non-root / —no-container-non-root: Container runs as UID 65532, no privilege escalation.
- —cap-add CAP: Repeatable; add back Linux capabilities (default drop ALL).
- —cap-drop CAP: Repeatable; override default drop policy.
Package installs like
apk add require root inside the container. Either leave container_non_root disabled for setup or prebuild an image. See Security & networking.For
kata-qemu-longhorn, the persistence wrapper requires /bin/sh, tar, and mount in the image. Use a Debian/Ubuntu-like base (or ensure those tools are installed).For
kata-qemu-longhorn, the main container runs privileged to allow bind mounts for persistence.For
kata-qemu-longhorn, the wrapper preserves the image ENTRYPOINT+CMD by default. Use --entrypoint and/or --cmd to override.