Skip to content

Containers for CLI Tools:When a Box Helps

Not every CLI needs a container. Some benefit from one: complex native dependencies, locked runtimes, or identical behaviour in CI and laptops. Containers are a distribution and isolation tool, not automatic security.

When a Container Earns Its Keep

Good reasons:

  • Native libraries that are painful to install
  • Guaranteed runtime version
  • CI jobs that should not pollute the runner
  • Customers who already standardise on images

Bad reasons:

  • "Everyone uses Docker"
  • Hiding a broken install story forever
  • Running as root because the Dockerfile was copied from a wiki

Prefer a plain package (pip/npm/brew) when it works.

Pin What You Build From

  • Pin base image digests or immutable tags
  • Rebuild deliberately; do not float on latest
  • Scan images in CI when you publish them
  • Prefer minimal bases (distroless/alpine/slim) when compatible

Your image is a dependency tree in a trench coat.

Run as Non-Root

Drop root in the final image. Mount only the directories the tool needs. Read-only root filesystem when practical. Least privilege applies inside containers too.

Keep the Attack Surface Small

  • Multi-stage builds so compilers never ship
  • No leftover secrets in layers (docker history remembers)
  • Explicit entrypoint; avoid kitchen-sink images
  • Document required volume mounts and ports (usually none for CLIs)

Version and Sign Like Other Artefacts

Tag images with the same version as the CLI release. Publish digests. Sign or attest when your pipeline supports it. Users should verify what they pull.

Document Escape Hatches

Explain how to run with local files mounted, how to pass config, and when not to use the image. A container that fights host keychains and sockets needs clear guidance.

Closing Thoughts

Containers can make CLI distribution boring and repeatable. Pin bases, run as non-root, keep layers lean, and still offer a non-container install when you can. The box should help users, not become the product.