Bash + Docker
Scripting Docker operations from Bash — container entrypoints, health-check waits, cleanup automation, and building images in CI.
Writing a Proper Docker Entrypoint Script
#!/usr/bin/env bash
set -euo pipefail
# Wait for a dependency before starting the main process
until nc -z "$DB_HOST" "$DB_PORT"; do
echo "Waiting for database at $DB_HOST:$DB_PORT..."
sleep 1
done
echo "Database is available"
# exec replaces THIS shell process with the app, so signals (SIGTERM) reach it directly
exec "$@"
ENTRYPOINT ["/entrypoint.sh"]
CMD ["node", "server.js"]
exec "$@" is critical: without it, the app runs as a child of the bash script, and the script (PID 1 in the container) doesn’t forward SIGTERM to it — leading to slow, forced (SIGKILL) shutdowns instead of graceful ones.
Cleaning Up Docker Resources
#!/usr/bin/env bash
set -euo pipefail
echo "Removing stopped containers..."
docker container prune -f
echo "Removing dangling images..."
docker image prune -f
echo "Removing unused volumes..."
docker volume prune -f
echo "Removing images older than 30 days not used by a running container..."
docker images --format '{{.ID}} {{.CreatedAt}}' | while read -r id created; do
AGE_DAYS=$(( ($(date +%s) - $(date -d "$created" +%s)) / 86400 ))
if [ "$AGE_DAYS" -gt 30 ]; then
docker rmi "$id" 2>/dev/null || true
fi
done
Waiting for a Container to Be Healthy
CONTAINER="myapp"
MAX_WAIT=60
ELAPSED=0
until [ "$(docker inspect -f '{{.State.Health.Status}}' "$CONTAINER" 2>/dev/null)" = "healthy" ]; do
if [ "$ELAPSED" -ge "$MAX_WAIT" ]; then
echo "Container did not become healthy in time" >&2
docker logs --tail 50 "$CONTAINER"
exit 1
fi
sleep 2
((ELAPSED += 2))
done
echo "$CONTAINER is healthy"
Scripting Builds and Pushes
#!/usr/bin/env bash
set -euo pipefail
IMAGE="myrepo/myapp"
TAG="${1:-$(git rev-parse --short HEAD)}"
docker build -t "$IMAGE:$TAG" -t "$IMAGE:latest" .
docker push "$IMAGE:$TAG"
docker push "$IMAGE:latest"
echo "Pushed $IMAGE:$TAG"
Extracting Info From Running Containers
docker inspect --format '{{.State.Pid}}' myapp # host PID of the container's main process
docker inspect --format '{{.NetworkSettings.IPAddress}}' myapp
docker ps --filter "status=exited" --format "{{.Names}}: {{.Status}}"
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
Running a Command Inside a Container From a Script
docker exec myapp printenv NODE_ENV
docker exec -it myapp /bin/sh -c "cat /app/config.json"
RESULT=$(docker exec myapp curl -sf http://localhost:8080/health)
echo "Health check result: $RESULT"
A Deploy Script Pattern: Rolling Container Restart
#!/usr/bin/env bash
set -euo pipefail
IMAGE="myrepo/myapp:latest"
echo "Pulling latest image..."
docker pull "$IMAGE"
echo "Starting new container alongside the old one..."
docker run -d --name myapp-new -p 8081:8080 "$IMAGE"
until curl -sf http://localhost:8081/health > /dev/null; do
echo "Waiting for new container to be healthy..."
sleep 2
done
echo "New container healthy — switching traffic and removing old one"
docker stop myapp-old 2>/dev/null || true
docker rm myapp-old 2>/dev/null || true
docker rename myapp myapp-old 2>/dev/null || true
docker rename myapp-new myapp
Production Considerations
- Always
exec "$@"at the end of an entrypoint script — otherwise the container’s main process never receivesSIGTERMcorrectly, and Kubernetes/Docker will forciblySIGKILLit after the grace period on every shutdown. - Prefer Docker’s built-in
HEALTHCHECK+docker inspect .State.Health.Statusover hand-rolledcurlpolling where possible — it’s visible to orchestrators too. - Automate image/volume cleanup on build/CI hosts — unused Docker images and volumes are a common, silent source of disk pressure on long-running CI runners.
Quick Interview Answer
“Bash and Docker meet mainly in entrypoint scripts and CI/deployment automation. The critical entrypoint pattern is ending with
exec \"$@\"so the containerized app becomes PID 1 directly and receives SIGTERM correctly on shutdown — without it, the wrapping shell absorbs the signal and the app gets hard-killed instead. Beyond that, scripts commonly wait for dependencies (nc -z), poll container health (docker inspect .State.Health.Status), and automate build/push/cleanup in CI pipelines.”
Common Mistakes
- Forgetting
execin an entrypoint script, causing slow/forced container shutdowns instead of graceful ones. - Hand-rolling health-check polling instead of using Docker’s native
HEALTHCHECKdirective where the orchestrator can act on it too. - Never running image/volume/container pruning, letting disk usage grow unbounded on build hosts over time.
Add More Questions to This Guide
Know a question that should be here? Share it and help the community!
Open Google Form