Skip to content

Releases: jkandasa/containerctl

v1.13.0

Choose a tag to compare

@github-actions github-actions released this 26 Aug 14:50

Fixed

  • exec into 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 on SIGWINCH.
  • cp within 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.4 pins a tag (refused when it is not greater). --version=devel follows the rolling main-branch pre-release and compares the remote commit (from the release body) to this binary's devel-<sha> so the same devel build is not downloaded again. --check reports 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 at SRC_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 allows cp only when serve.exec.enabled is set; host-path copies are rejected there (stream with -, or copy container-to-container). Both containers must be in serve.exec.allowed when that list is set.

devel (rolling)

devel (rolling) Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 26 Aug 14:50

Rolling release from the main branch. Built from commit 4944328.

Not guaranteed to be stable. Use a tagged release for production.

v1.12.0

Choose a tag to compare

@github-actions github-actions released this 23 Aug 08:52

Added

  • apply --follow, enable --follow, and repull --follow stream logs after the container is created, recreated, or started. Requires exactly one selected container (apply also rejects --dry-run). Follows only if that container was actually started; otherwise prints Not following logs: ... and exits 0.
  • GitHub Actions lint workflow (.github/workflows/lint.yaml) runs golangci-lint on pull requests and pushes to main. .golangci.yml applies the standard exclusion presets so idiomatic defer Close() / fmt.Fprint patterns 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 --follow or --tail.
  • logs --info: print the engine log driver, file path, size, and last-modified age. Not compatible with --follow, --tail, or --clear.

Fixed

  • enable now starts a stopped container when the config hash has not changed. Previously it only recreated on drift, so a container stopped by disable stayed stopped after enable.

v1.11.0

Choose a tag to compare

@github-actions github-actions released this 05 Aug 09:05

Added

  • stack [file] [--unset] — set or show the default stack file for subsequent commands (like oc project). No args prints the current path; a path is stored as an absolute path in $XDG_CONFIG_HOME/containerctl/config.json; --unset clears it. Explicit -f/--file still overrides the saved default for that invocation. Resolution order: -f → saved stack path → ./stack.yaml.
  • command and entrypoint accept 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 8080 is equivalent to command: ["serve", "--port", "8080"].

Changed

  • Removed the version subcommand. Use containerctl --version instead (prints version, build date, Go version, OS/arch). Engine reachability details are no longer included.

v1.10.0

Choose a tag to compare

@github-actions github-actions released this 02 Aug 13:13

Added

  • -l / --label on apply, status, update, start, stop, restart, and down to select containers by stack YAML labels: (kubectl-style). Supports KEY (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 relative data_path are resolved against the stack file's directory (not the process CWD), so containerctl -f /path/to/stack.yaml finds 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_file parsing: surrounding quotes on values are stripped, optional export prefixes are accepted, and lines with a key but no = pass through the host environment when set (same as docker run --env-file).

v1.9.0

Choose a tag to compare

@github-actions github-actions released this 29 Jul 05:16

Added

  • generate [name...]: render a stack.yaml from containers that already exist on the host, for adopting containers created by docker run or docker compose. -O FILE writes to a file (mode 0600, because env: 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/none network modes, tmpfs options) is reported as a WARN: on stderr. Output is deterministic, and for containers containerctl already manages generate followed by apply --dry-run reports no changes.
  • Web terminal: generate added to the command allowlist. generate -O FILE requires serve.edit.enabled and an absolute .yaml/.yml target, since it writes to the server's disk.

Security

  • /api/v1/file now honours serve.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 when edit was disabled, which SPEC §12.15 documents as a minimal-privilege deployment. Both GET and PUT now return 403 unless editing is explicitly enabled.
  • The file endpoint accepts only .yaml/.yml paths. This replaces the v1 decision to leave file selection entirely to filesystem permissions (SPEC §12.9). use can point a session at any stack file, so the target cannot be restricted to one directory, but limiting the extension keeps shell profiles, authorized_keys and unit files out of reach of a hijacked session. The browser editor is unaffected: it only ever opens stack files.

Changed

  • diff command removed. Use apply --dry-run instead; same output and exit codes (0 no changes, 3 changes pending).
  • Web terminal help now lists --follow for update, restart and start, which support it but did not advertise it.

Fixed

  • update: containers persistently disabled with containerctl 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 --apply reports 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: --follow triggered whenever any container had a pending update, even if none was actually recreated, so following a disabled or failed container died with Error: container "<name>" not found and exit 1. It now attaches only to a container that was really updated, and otherwise prints Not 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, printed Error: terminated signal received and exited 1. Cancellation via the command's own signal handler is now a clean exit 0, matching status --watch.

v1.8.0

Choose a tag to compare

@github-actions github-actions released this 01 Jun 05:44

Added

  • status now 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. Both created_at and started_at are included in -o json|yaml output.
  • status --stats now 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|yaml groups all live-usage fields under a stats object: cpu_percent, cpu_throttled_periods, cpu_throttled_time, cpu_throttled_time_ns, memory_used, memory_used_bytes, and memory_fail_count (last three omitted when zero/absent).

Fixed

  • status: containers in the disabled state (disabled via state file, container still present on host) now correctly show CREATED and UPTIME. Previously both were always - because the InspectContainer detail was not consulted for that code path.
  • status: containers with disabled: true in 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, and last_restart are 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

Choose a tag to compare

@github-actions github-actions released this 27 May 16:10

Added

  • images [name...] — positional arguments now filter images by name or tag substring.
  • NO_COLOR environment variable is now honoured in addition to --no-color.
  • images, volumes, networks, and prune no longer require a stack.yaml to be present. The runtime is determined from --runtime/--socket flags (defaulting to Docker). If a stack file is found it is still used for runtime/socket settings; images --unused and prune --images will also cross-reference stack declarations when a file is present.
  • Web terminal now supports color themes: dark (default), light, and auto (follows system prefers-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-hash labels were non-deterministic for any container using env: or labels: (map key iteration order in JSON). This caused unnecessary container recreation on every apply after 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 json calls 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=expired with a clear message instead of showing a stuck error.

Changed

  • --output flag default value changed from text to console (-o console|json|yaml). The behaviour is identical; console better describes the formatted table output.
  • check-update renamed to update — shorter and less redundant (it already implies checking).
  • upgrade renamed to repull — unambiguous: force-pulls the image and recreates the container without consulting the config hash.
  • stop, start, restart Use strings updated to <name...> | --all to make it clear that at least one name or --all is required.
  • restart Short description updated to "Recreate containers from current config (stop, remove, create, start)".
  • start Short description updated to "Start stopped containers without reconciling".
  • stop Short description updated to "Stop containers; they stay on disk and restart on next apply".
  • logs --tail default changed from 0 to -1; semantics are now -1 = all lines (default), 0 = no lines, N = last N lines. Previously 0 meant all, which was counter-intuitive.
  • --interval on status now returns an error when used without --watch.
  • prune Short and Long descriptions now say "host-wide" to make clear the command is not project-scoped.
  • volumes --size description leads with the cost ("triggers a daemon-side scan; may be slow") rather than burying it.
  • serve --address description now includes usage examples (:8080 or 127.0.0.1:9090).
  • Removed phantom --verbose / -v flag (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

Choose a tag to compare

@github-actions github-actions released this 26 May 13:23

Added

  • group_add field in stack.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 on apply.
  • 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 --follow and restart --follow stream container logs immediately after the container is running. Requires a single container name (not compatible with --all or multiple names).
  • check-update --follow streams logs after --apply completes for a single container. Requires both --apply and exactly one container name argument.
  • images [--unused] — list all local images with tags, size, age, and attached container names. --unused filters to images not referenced by any running container or stack declaration. -o json|yaml includes per-image container list with name and state.
  • volumes [--unused] [--size] — list local volumes with driver and attached containers. --unused filters to dangling (unmounted) volumes. --size fetches 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 (-1 when the driver does not report). -o json|yaml additionally includes the host mountpoint and per-container mount details (source, destination, read_only).
  • networks [--unused] — list user-defined networks (system networks bridge, host, none are excluded). --unused filters to networks not connected to any container. -o json|yaml includes 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; --all is equivalent to --images --volumes --networks. --dry-run previews what would be removed without acting. When stdin is not a terminal, --force is required to prevent accidental deletion in scripts.
  • status -o json|yaml now includes networks (name, ip_address, gateway) and mounts (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|yaml includes the host mountpoint path and an optional size field (populated only when --size is passed).
  • networks -o json|yaml fields are now consistently snake_case with explicit JSON tags (id, name, driver, labels).
  • ListContainers now 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 across status, volumes, networks, and images.

Fixed

  • exec no longer leaves the terminal in raw mode (invisible input) after the container exits with a non-zero code. os.Exit bypasses deferred functions; the terminal state is now restored explicitly before exit.
  • serve browser terminal no longer shows a double prompt after a blocked or unrecognised command. The error message handler no longer calls writePrompt() — the done message that always follows handles the prompt.
  • volumes -o json no longer silently omits the size field for empty volumes. Changed from int64 with omitempty (which drops 0) to *int64 — nil means not fetched (omitted), 0 means genuinely empty, -1 means driver does not report.
  • prune --images no 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's ImageID field as the primary check, with tag name as a fallback.

v1.5.0

Choose a tag to compare

@github-actions github-actions released this 18 May 06:41

Added

  • status --watch (-w) refreshes the output repeatedly with flicker-free in-place rendering. Default interval is 2s; --interval accepts Go duration strings (500ms, 5s, 1m, etc.). Exits cleanly on Ctrl+C.
  • status --stats shows live CPU and memory usage. Omitted by default to keep status fast; collecting stats adds ~1-2s.
  • network_aliases field in stack.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.
  • version now shows container engine details: engine version, API version, OS/arch, and kernel version. Supports -o json|yaml for structured output.

Changed

  • status now 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

  • logs no 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.
  • status ports 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.