Driving Docker and Container CLIs
Container CLIs expose powerful lifecycle operations, but a robust wrapper must know whether the daemon is available, which image is intended, what state the container is in, and how replacement and cleanup are scoped.
Learning objectives
By the end of this lesson
- Verify Docker CLI and daemon separately.
- Use inspect rather than table parsing.
- Make run configuration explicit.
- Protect environment and credential data.
- Recognize when a higher-level orchestrator is more appropriate.
1. Treat container CLIs as structured state machines
Docker commands create, inspect, start, stop, tag, push, and remove resources. Reliable shell automation should inspect current state and use deterministic names, labels, and image references.
flowchart TD I["image ref"] --> C["container create/run"] C --> H["health / status"] H --> L["logs / inspect"] L --> R["cleanup / replace"]
2. Verify the CLI and daemon separately
command -v docker >/dev/null 2>&1 || {
printf 'docker CLI missing\n' >&2
exit 69
}
docker info >/dev/null 2>&1 || {
printf 'docker daemon unavailable or inaccessible\n' >&2
exit 69
}
Finding the CLI executable does not prove the daemon is running or the current user has permission to access it.
3. Prefer immutable image identifiers for deployment
image='example/api@sha256:0123456789abcdef...'
docker pull "$image"
Tags such as latest are mutable references. For
reproducible promotion, pin a digest or an otherwise immutable
release identifier when your workflow supports it.
4. Use inspect formatting instead of parsing table output
status=$(
docker inspect \
--format '{{.State.Status}}' \
my-container
)
printf 'status=%s\n' "$status"
Human-friendly docker ps columns are not a stable
parsing interface.
5. Make run configuration explicit
docker run \
--detach \
--name api \
--restart unless-stopped \
--publish 8080:8080 \
--env-file ./api.env \
"$image"
Names, ports, restart policy, environment, volumes, networks, and image reference are part of the deployment contract.
6. Protect environment and secret values
Environment files are convenient but may contain credentials. Restrict permissions, keep them out of source control, and prefer your platform's secret facilities when appropriate.
Debug tracing with set -x can expose environment
values or registry credentials.
7. Replace containers deliberately
if docker container inspect api >/dev/null 2>&1; then
docker stop --time 20 api
docker rm api
fi
docker run --detach --name api "$image"
For production systems, orchestrators often provide safer rolling replacement than stop/remove/run logic. This pattern is mainly appropriate for bounded single-host automation.
8. Capture diagnostics without losing exit status
if ! docker start api; then
status=$?
docker logs --tail 100 api >&2 || true
exit "$status"
fi
When a lifecycle command fails, logs and inspect output are valuable context, but preserve the primary failure status first.
9. Cleanup commands can be destructive
docker image prune --filter 'until=168h'
Prune commands can remove resources other workflows still need. Preview and label resources so cleanup operates on an intentional scope.
10. Use higher-level container tools when the topology grows
Once several containers, networks, volumes, health dependencies, and rollout order are involved, Docker Compose or an orchestrator is usually clearer than dozens of individual Bash-wrapped Docker commands.
11. Hands-on lab: container preflight and inspect helper
mkdir -p "$HOME/devops-academy/bash/chapter15/lesson02"
cd "$HOME/devops-academy/bash/chapter15/lesson02"
cat > docker-check.sh <<'EOF'
#!/usr/bin/env bash
set -u
name=${1:-}
command -v docker >/dev/null 2>&1 || {
printf 'docker CLI missing\n' >&2
exit 69
}
docker info >/dev/null 2>&1 || {
printf 'docker daemon unavailable\n' >&2
exit 69
}
if [[ -z $name ]]; then
printf 'docker=ready\n'
exit 0
fi
if docker container inspect "$name" >/dev/null 2>&1; then
state=$(docker inspect --format '{{.State.Status}}' "$name")
image=$(docker inspect --format '{{.Config.Image}}' "$name")
printf 'container=%s state=%s image=%s\n' "$name" "$state" "$image"
else
printf 'container=%s state=absent\n' "$name"
fi
EOF
chmod u+x docker-check.sh
./docker-check.sh
Verification checklist
12. Knowledge check
Question 1. Why is a Docker tag such as
latest weaker for reproducibility?
Question 2. Why prefer
docker inspect --format?
Question 3. Does the Docker CLI being installed prove containers can be managed?
Question 4. When should Bash stop managing individual container lifecycle commands?
13. Summary
Reliable Docker scripting separates CLI and daemon readiness, uses deterministic names and immutable image references where possible, reads state through inspect, protects secrets, preserves failure diagnostics, and avoids overly broad cleanup.
14. Further reading
- Docker CLI reference — run, inspect, logs, image pull, container lifecycle.
- Docker image digest and tagging documentation.
- Docker Compose documentation.
- Docker security guidance for credentials and daemon access.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.