Skip to content

nkapp containers prune ​

Remove stopped, aged-out, unclaimed containers. This is the container analogue of nkapp volumes prune and nkapp images prune, and the three share one selection authority with the Hub, so the CLI and the dashboard can never disagree about what is prunable.

bash
nkapp containers prune [options]

A side-effect-free preview is always rendered first — a table of the containers that would be pruned plus a skipped count — before anything is removed. --dry-run stops there.


What gets pruned ​

A container is pruned only when all of these hold:

GuardRule
Stateexited or created. A running, paused or restarting container is never touched, and no flag changes that.
AgeIt stopped at least --older-than ago (default 24h). An exited container is aged on its exit time, never its creation time, so a service that ran for days and exited a second ago is never swept. If the exit time cannot be determined, the container is skipped.
OwnershipIt is labeled for this project + environment, or is an unlabeled legacy container named nk-<project>-<env>-…. A container labeled (or named) for another project or environment is never removed.
Not declaredIt is not a service, database or system container declared in nanokit.yml. A service you deliberately stopped may be exited and months old; it is never garbage.

Options ​

FlagTypeDefaultDescription
-e, --env <env>stringlocalEnvironment to use
--ssh <host>string—Target an explicit host (e.g. root@1.2.3.4), overriding the environment's configured deploy target — prunes stranded / cross-host stale containers
-p, --port <port>number22SSH port (used with --ssh)
-i, --ssh-key <path>string—SSH private key override (used with --ssh)
--older-than <dur>string24hMinimum time since the container stopped — 30s, 15m, 24h, 7d, or 0 to disable the age gate
--unlabeledbooleanfalseAlso prune containers carrying no Nanokit labels at all
--dry-runbooleanfalseList what would be pruned, change nothing
-y, --yesbooleanfalseSkip the confirmation prompt

An unparseable --older-than value fails the command before any connection is opened, rather than being guessed at.


Why --unlabeled exists ​

Nanokit's cron scheduler creates a fresh container per job fire, and it exposes no option to label them — so those containers are auto-named (suspicious_jackson, …) and carry no Nanokit labels. When the scheduler's own reap fails, the stray is invisible to every label-scoped Nanokit command and accumulates forever. Left unchecked this class of leak has reached thousands of container directories and taken down a Docker daemon.

--unlabeled is opt-in because it is the one mode that reaches containers Nanokit did not demonstrably create. Always inspect the --dry-run table first.


Not wired into reconcile ​

up and deploy do not run this prune. Removing containers mid-deploy is a surprise you did not ask for, and the sweep that actually catches the cron strays requires --unlabeled, which is far too broad to run implicitly. Detection is automatic (nkapp doctor → Container Hygiene); removal stays an explicit, reviewable command.


Examples ​

bash
# Review what would be removed locally
nkapp containers prune --dry-run

# Prune stale containers on the staging host
nkapp containers prune -e stage -y

# Include the unlabeled cron strays — inspect first
nkapp containers prune --unlabeled --dry-run
nkapp containers prune --unlabeled -y

# Target a stranded host directly
nkapp containers prune --ssh root@1.2.3.4 -i ~/.ssh/id_rsa --dry-run

# Sweep anything that stopped more than an hour ago
nkapp containers prune --older-than 1h

Execution is best-effort: per-container failures are aggregated and reported, and any failure exits non-zero.