Skip to content

Container Management

erickochen edited this page Mar 27, 2026 · 2 revisions

Press C on any host to see all Docker or Podman containers (lightweight, isolated application environments running on the remote server) over SSH. Start, stop and restart without leaving purple. No agent to install, no web UI, no extra ports.

How it works

Purple runs a single SSH command to detect the container runtime and list all containers in one call. The detection command uses sentinel markers (##purple:docker##, ##purple:podman##, ##purple:none##) to identify which runtime is available:

if command -v docker >/dev/null 2>&1; then
  echo '##purple:docker##' && docker ps -a --format '{{json .}}'
elif command -v podman >/dev/null 2>&1; then
  echo '##purple:podman##' && podman ps -a --format '{{json .}}'
else echo '##purple:none##'; fi

This keeps the detection and listing to a single SSH round-trip. On subsequent refreshes, purple reuses the cached runtime and skips detection, running docker ps -a --format '{{json .}}' (or podman) directly.

Container output is parsed as NDJSON (newline-delimited JSON. Each line is a separate JSON object representing one container). Invalid lines (MOTD banners (message-of-the-day text servers often print on login), blank lines) are silently skipped by the parser.

Security

Container IDs are validated with an ASCII allowlist (alphanumeric, hyphen, underscore, dot) before being passed to shell commands. This prevents shell injection (where a malicious container name like ; rm -rf / could execute arbitrary commands) through crafted container names or IDs.

Error handling

Purple translates common SSH and Docker/Podman errors into user-friendly messages:

  • "Docker or Podman not found" when command -v fails
  • "Permission denied. Is your user in the docker group?" for access errors
  • "Container daemon is not running." when the daemon socket is missing
  • "Host unreachable." for network errors

If the ps command fails but the runtime was detected, purple preserves the detected runtime in the error so it doesn't need to re-detect on the next refresh.

Requirements

The SSH user must be able to run Docker or Podman commands (e.g. member of the docker group). No root or special permissions required beyond that. Nothing is installed on the remote host.

Keybindings

Key Action
j / k Navigate
s Start container
x Stop container (confirms first)
r Restart container (confirms first)
R Refresh container list
PgDn / PgUp Page down / up
q / Esc Back

Stop and restart require a y/N confirmation before executing to prevent accidental disruption.

Caching

Container data is cached in ~/.purple/container_cache.jsonl (JSONL format. Each line is a JSON object containing the host alias, runtime type and container list). The cache is loaded at startup and saved after each fetch. Cached container data appears in the detail panel with checkmark/cross icons showing running/stopped state. This means you see container status for previously fetched hosts without re-connecting.

Works through

  • ProxyJump chains (connecting through one or more bastion/jump hosts to reach the target server. Uses ssh -F <config> so your full config applies)
  • Password-protected hosts (askpass integration, same as regular connections)
  • Active tunnels

How purple compares

  • vs. Portainer / Dockge: purple manages containers over plain SSH. No agent. No web UI. No extra ports to open
  • vs. Lazydocker: Lazydocker manages Docker locally. purple manages Docker and Podman on remote servers over SSH without installing anything on each host

Clone this wiki locally