Repository navigation
Releases: jkandasa/containerctl
Releases · jkandasa/containerctl
Release list
v1.13.0
Fixed
execinto bash/sh used an 80-column TTY and overwrote the current line instead of wrapping. Stdin is fd 0, but size handling treated 0 as "unset", so the host window size was never applied. The host size is now set on create/attach, applied again after the process starts (with retries), and updated onSIGWINCH.cpwithin the same container hung: the engine holds a filesystem lock for archive GET and PUT, so streaming the source into the destination on one container deadlocked. Same-container copies now drain the source archive (via a temp file) and close it before writing.
Added
upgrade: replace this binary with a newer GitHub release. No flags installs the latest stable tag when it is greater than the current version.--version=v1.2.4pins a tag (refused when it is not greater).--version=develfollows the rolling main-branch pre-release and compares the remote commit (from the release body) to this binary'sdevel-<sha>so the same devel build is not downloaded again.--checkreports without replacing (exit 3 if an update is available). CLI-only.cp NAME:SRC DEST/cp SRC NAME:DEST/cp SRC_NAME:SRC DEST_NAME:DEST: copy files and directories between a managed container and the host, or between two managed containers (docker-cp semantics). Distinct containers stream a tar through the engine; same-container copies drain to a host temp file first to avoid an engine deadlock. Works on running or stopped containers. Flags:-a/--archive(preserve uid/gid when copying into a container),-L/--follow-link(follow a symlink atSRC_PATH),--overwrite(allow replacing a directory with a file in the container),-q/--quiet.-streams a tar archive to stdout or from stdin. The web terminal allowscponly whenserve.exec.enabledis set; host-path copies are rejected there (stream with-, or copy container-to-container). Both containers must be inserve.exec.allowedwhen that list is set.
devel (rolling)
Rolling release from the main branch. Built from commit 4944328.
Not guaranteed to be stable. Use a tagged release for production.
v1.12.0
Added
apply --follow,enable --follow, andrepull --followstream logs after the container is created, recreated, or started. Requires exactly one selected container (applyalso rejects--dry-run). Follows only if that container was actually started; otherwise printsNot following logs: ...and exits 0.- GitHub Actions lint workflow (
.github/workflows/lint.yaml) runsgolangci-linton pull requests and pushes tomain..golangci.ymlapplies the standard exclusion presets so idiomaticdefer Close()/fmt.Fprintpatterns are not flagged. logs --clear: empty the current container log file in place without stopping or recreating the container. Works with file-backed drivers (json-file,k8s-file). Not compatible with--followor--tail.logs --info: print the engine log driver, file path, size, and last-modified age. Not compatible with--follow,--tail, or--clear.
Fixed
enablenow starts a stopped container when the config hash has not changed. Previously it only recreated on drift, so a container stopped bydisablestayed stopped afterenable.
v1.11.0
Added
stack [file] [--unset]— set or show the default stack file for subsequent commands (likeoc project). No args prints the current path; a path is stored as an absolute path in$XDG_CONFIG_HOME/containerctl/config.json;--unsetclears it. Explicit-f/--filestill overrides the saved default for that invocation. Resolution order:-f→ savedstackpath →./stack.yaml.commandandentrypointaccept a Compose-style string as well as a list. A string is shell-split into argv (quotes and backslashes supported); it is not run by a shell. Example:command: serve --port 8080is equivalent tocommand: ["serve", "--port", "8080"].
Changed
- Removed the
versionsubcommand. Usecontainerctl --versioninstead (prints version, build date, Go version, OS/arch). Engine reachability details are no longer included.
v1.10.0
Added
-l/--labelonapply,status,update,start,stop,restart, anddownto select containers by stack YAMLlabels:(kubectl-style). SupportsKEY(exists),!KEY(absent),KEY=VALUE,KEY!=VALUE, comma-separated terms, and repeated flags (all AND). Documented in README and SPEC §5. Example:containerctl apply -l release,environment=production.
Fixed
env_file: relative paths and relativedata_pathare resolved against the stack file's directory (not the process CWD), socontainerctl -f /path/to/stack.yamlfinds env files and volume sources correctly from any working directory.env_file: a single string is now accepted in addition to a list (Compose-compatible), e.g.env_file: secrets.env.env_fileparsing: surrounding quotes on values are stripped, optionalexportprefixes are accepted, and lines with a key but no=pass through the host environment when set (same asdocker run --env-file).
v1.9.0
Added
generate [name...]: render astack.yamlfrom containers that already exist on the host, for adopting containers created bydocker runordocker compose.-O FILEwrites to a file (mode0600, becauseenv:values often carry credentials) instead of stdout; the host is never modified. Settings equal to the image defaults are omitted,containerctl.*/com.docker.compose.*labels are dropped, anonymous volumes are emitted as commented-out entries, and anything unrepresentable (foreign networks,host/nonenetwork modes, tmpfs options) is reported as aWARN:on stderr. Output is deterministic, and for containers containerctl already managesgeneratefollowed byapply --dry-runreports no changes.- Web terminal:
generateadded to the command allowlist.generate -O FILErequiresserve.edit.enabledand an absolute.yaml/.ymltarget, since it writes to the server's disk.
Security
/api/v1/filenow honoursserve.edit.enabled. The endpoint was gated only by the session cookie, so any authenticated browser session could read or overwrite any file the server process could reach, including wheneditwas disabled, which SPEC §12.15 documents as a minimal-privilege deployment. BothGETandPUTnow return403unless editing is explicitly enabled.- The file endpoint accepts only
.yaml/.ymlpaths. This replaces the v1 decision to leave file selection entirely to filesystem permissions (SPEC §12.9).usecan point a session at any stack file, so the target cannot be restricted to one directory, but limiting the extension keeps shell profiles,authorized_keysand unit files out of reach of a hijacked session. The browser editor is unaffected: it only ever opens stack files.
Changed
diffcommand removed. Useapply --dry-runinstead; same output and exit codes (0 no changes, 3 changes pending).- Web terminal
helpnow lists--followforupdate,restartandstart, which support it but did not advertise it.
Fixed
update: containers persistently disabled withcontainerctl disable(state file) were registry-checked, listed as having an update, then skipped silently by--apply. They now carry a(disabled)suffix in the STATUS column, are excluded from the apply set, and--applyreports which containers were held back and how to release them instead of printing the generic "Major version updates require manual tag changes" message.update --apply --follow:--followtriggered whenever any container had a pending update, even if none was actually recreated, so following a disabled or failed container died withError: container "<name>" not foundand exit 1. It now attaches only to a container that was really updated, and otherwise printsNot following logs: <name> was not updated.and exits 0.update --apply: a container that disappeared from the stack file between the check and the apply was skipped without a word; it now reports the skip on stderr.update --apply --follow: stopping the log stream with Ctrl-C, which is the documented way to stop it, printedError: terminated signal receivedand exited 1. Cancellation via the command's own signal handler is now a clean exit 0, matchingstatus --watch.
v1.8.0
Added
statusnow shows a CREATED column (between PORTS and UPTIME) displaying how long ago the container was created (e.g.16d 4h). Previously only uptime (time since last start) was shown, and it was incorrectly derived from the container creation timestamp. UPTIME now correctly reflects the time since the container last started. Bothcreated_atandstarted_atare included in-o json|yamloutput.status --statsnow includes CPU and memory throttling data. A THROTTLE column appears in the console table when any container has been throttled (e.g.cpu:42 mem:0).-o json|yamlgroups all live-usage fields under astatsobject:cpu_percent,cpu_throttled_periods,cpu_throttled_time,cpu_throttled_time_ns,memory_used,memory_used_bytes, andmemory_fail_count(last three omitted when zero/absent).
Fixed
status: containers in thedisabledstate (disabled via state file, container still present on host) now correctly show CREATED and UPTIME. Previously both were always-because theInspectContainerdetail was not consulted for that code path.status: containers withdisabled: truein YAML that are still running on the host now show full runtime data (CREATED, UPTIME, ports, networks, mounts) with a note "disabled: true in YAML (apply will remove)". Previously all runtime fields showed-, giving no visibility into what was still running.status -o json|yaml:created_at,started_at, andlast_restartare now always emitted in the host's local timezone (e.g.2026-05-31T06:51:27+05:30) for consistent, human-readable output.
v1.7.0
Added
images [name...]— positional arguments now filter images by name or tag substring.NO_COLORenvironment variable is now honoured in addition to--no-color.images,volumes,networks, andpruneno longer require astack.yamlto be present. The runtime is determined from--runtime/--socketflags (defaulting to Docker). If a stack file is found it is still used for runtime/socket settings;images --unusedandprune --imageswill also cross-reference stack declarations when a file is present.- Web terminal now supports color themes:
dark(default),light, andauto(follows systemprefers-color-scheme). A toggle button (◐/☾/☼) is available in the top bar; the choice is persisted in localStorage and applies to the terminal (xterm), editor (CodeMirror), and login page.
Fixed
- Critical:
containerctl.config-hashlabels were non-deterministic for any container usingenv:orlabels:(map key iteration order in JSON). This caused unnecessary container recreation on everyapplyafter a tool restart. Hash computation is now stable across runs. - Container names were missing from tab completion in the web terminal (regression after syntax highlighting was added). Internal
status --output jsoncalls used for completion now always receive clean JSON. - Expired or invalid sessions on long-lived WebSocket connections (terminal, exec, logs) now properly redirect the browser to
/login?error=expiredwith a clear message instead of showing a stuck error.
Changed
--outputflag default value changed fromtexttoconsole(-o console|json|yaml). The behaviour is identical;consolebetter describes the formatted table output.check-updaterenamed toupdate— shorter and less redundant (it already implies checking).upgraderenamed torepull— unambiguous: force-pulls the image and recreates the container without consulting the config hash.stop,start,restartUse strings updated to<name...> | --allto make it clear that at least one name or--allis required.restartShort description updated to "Recreate containers from current config (stop, remove, create, start)".startShort description updated to "Start stopped containers without reconciling".stopShort description updated to "Stop containers; they stay on disk and restart on next apply".logs --taildefault changed from0to-1; semantics are now-1= all lines (default),0= no lines,N= last N lines. Previously0meant all, which was counter-intuitive.--intervalonstatusnow returns an error when used without--watch.pruneShort and Long descriptions now say "host-wide" to make clear the command is not project-scoped.volumes --sizedescription leads with the cost ("triggers a daemon-side scan; may be slow") rather than burying it.serve --addressdescription now includes usage examples (:8080or127.0.0.1:9090).- Removed phantom
--verbose / -vflag (was registered but never acted on). - Web terminal help text and command allowlist updated to reflect renames (
check-update→update,upgrade→repull). - Web terminal UI/UX improvements: active stack filename now shown in top bar, better initial spacing, improved focus styles, reconnect link on disconnect, more informative login error messages (including session expiry), and various polish.
v1.6.0
Added
group_addfield instack.yaml— adds supplementary GIDs to the container process without changing the user or primary group. Accepts a list of group IDs or names (e.g.["1500", "docker"]). Changes are included in the config hash and trigger recreation onapply.exec <name> [command...]— run a command inside a running container. Defaults to/bin/sh. Allocates a PTY and sets raw terminal mode when stdin is a terminal; window resize is forwarded automatically. Non-TTY invocations (piped stdin) run without a PTY.start --followandrestart --followstream container logs immediately after the container is running. Requires a single container name (not compatible with--allor multiple names).check-update --followstreams logs after--applycompletes for a single container. Requires both--applyand exactly one container name argument.images [--unused]— list all local images with tags, size, age, and attached container names.--unusedfilters to images not referenced by any running container or stack declaration.-o json|yamlincludes per-image container list with name and state.volumes [--unused] [--size]— list local volumes with driver and attached containers.--unusedfilters to dangling (unmounted) volumes.--sizefetches disk usage via the daemon's disk-usage endpoint (triggers a daemon-side scan) and adds a SIZE column; in JSON/YAML the size is in bytes (-1when the driver does not report).-o json|yamladditionally includes the hostmountpointand per-container mount details (source, destination, read_only).networks [--unused]— list user-defined networks (system networksbridge,host,noneare excluded).--unusedfilters to networks not connected to any container.-o json|yamlincludes per-network container list with IP address and gateway. The MANAGED column indicates networks created by containerctl.prune [--images] [--volumes] [--networks] [--all] [--dry-run] [--force]— remove unused local resources. At least one resource type must be selected;--allis equivalent to--images --volumes --networks.--dry-runpreviews what would be removed without acting. When stdin is not a terminal,--forceis required to prevent accidental deletion in scripts.status -o json|yamlnow includesnetworks(name, ip_address, gateway) andmounts(type, name, source, destination, read_only) for each container. Tmpfs mounts are excluded. These fields are omitted from the text table output.
Changed
- All JSON output is now indented with 2 spaces for readability (previously compact single-line).
- All YAML output uses 2-space indentation consistently across all commands.
volumes -o json|yamlincludes the hostmountpointpath and an optionalsizefield (populated only when--sizeis passed).networks -o json|yamlfields are now consistently snake_case with explicit JSON tags (id,name,driver,labels).ListContainersnow populates full mount details (ContainerMount: type, name, source, destination, read_only) and network details (ContainerNetworkInfo: name, ip_address, gateway) instead of plain string slices. This powers the enriched JSON output acrossstatus,volumes,networks, andimages.
Fixed
execno longer leaves the terminal in raw mode (invisible input) after the container exits with a non-zero code.os.Exitbypasses deferred functions; the terminal state is now restored explicitly before exit.servebrowser terminal no longer shows a double prompt after a blocked or unrecognised command. Theerrormessage handler no longer callswritePrompt()— thedonemessage that always follows handles the prompt.volumes -o jsonno longer silently omits thesizefield for empty volumes. Changed fromint64withomitempty(which drops0) to*int64—nilmeans not fetched (omitted),0means genuinely empty,-1means driver does not report.prune --imagesno longer marks images that are actively used by running containers as candidates for removal. Image matching now uses the short 12-character image ID from the container'sImageIDfield as the primary check, with tag name as a fallback.
v1.5.0
Added
status --watch(-w) refreshes the output repeatedly with flicker-free in-place rendering. Default interval is2s;--intervalaccepts Go duration strings (500ms,5s,1m, etc.). Exits cleanly on Ctrl+C.status --statsshows live CPU and memory usage. Omitted by default to keep status fast; collecting stats adds ~1-2s.network_aliasesfield instack.yaml— additional DNS names for a container on its connected networks. Useful for reaching a container via a custom hostname (e.g.db.backend) without changing its container name. Aliases are registered on every network the container joins and are included in the config hash so changes trigger recreation.versionnow shows container engine details: engine version, API version, OS/arch, and kernel version. Supports-o json|yamlfor structured output.
Changed
statusnow runs all per-container API calls (image meta, inspect, stats) in parallel, reducing wall-clock time from ~1s×N to ~1-2s regardless of container count.
Fixed
logsno longer shows garbage characters at the start of each line. Docker multiplexes stdout and stderr with 8-byte binary frame headers when the container has no TTY; the stream is now demultiplexed transparently before output. TTY containers are unaffected.statusports column no longer reorders between refreshes. The Docker API returns port bindings in non-deterministic order; bindings are now sorted by container port, protocol, host port, and host IP for stable output.