Automating Docker and Kubernetes Workflows with Bash
AI generated
Bash · Docker · Kubernetes · DevOps · kubectl
Automating Docker and Kubernetes Workflows with Bash
Health checks, rollout scripts and container monitoring in the shell

Container tooling like Docker and Kubernetes ships with powerful CLIs, but only Bash automation ties health checks, rollout control and alerting into a complete workflow. Teams that automate Docker and Kubernetes workflows with Bash cut down on manual intervention and make deployments traceable, repeatable and safe.

15 min read docker · kubectl · health checks · rollouts · monitoring Bash 4.x · 5.x · Linux · macOS · CI/CD

1. Why automate Docker and Kubernetes with Bash?

Anyone who wants to automate Docker and Kubernetes workflows with Bash has a pragmatic reason for it: both tools ship powerful CLIs, but without a script wrapper the orchestration is missing. A docker run in a CI pipeline without a health check, without rollback logic and without a timeout is not a deployment process, it is a manually triggered command. Only a Bash script that waits for health checks, monitors status, automatically rolls back on failure and logs every step turns this into a reproducible workflow.

The second reason is control over CLI output. docker inspect, kubectl get pods -o json and kubectl rollout status return machine readable output that Bash scripts can parse with jq, evaluate and translate into decision logic. Automating Docker and Kubernetes workflows with Bash means turning that output into conditions: wait for a container status of healthy, check whether all pods are Running, compare image tags before and after the rollout. This decision logic does not exist in the CLIs themselves, it belongs in Bash.

Third, Bash scripts are portable and independent of CI platform specific features. The same rollout script runs in GitHub Actions, GitLab CI, Jenkins and manually on the server. Anyone who implements Docker and Kubernetes workflow automation with Bash builds an automation layer that is not tied to a particular CI/CD platform.

2. Using the Docker CLI reliably in Bash scripts

The most important foundation for automating Docker and Kubernetes workflows with Bash is correctly evaluating exit codes. Docker CLI commands return exit code 1 on failure, so with set -euo pipefail in the script every Docker error automatically aborts the script. Parsing Docker output requires either --format with Go template syntax or docker inspect --format for structured field lookups. Both approaches are more reliable than parsing plain text output line by line with grep and awk, because they do not depend on the output formatting of a particular Docker version.

A common problem when automating Docker and Kubernetes workflows with Bash: race conditions during container startup. A docker run returns the container name as soon as the container has started, not once the application inside it is ready. Anyone who sends requests to the container right after docker run often hits a container that is running but whose service is still initializing. The solution is an active wait loop that polls the container's health check status and only continues once the status is healthy.


#!/usr/bin/env bash
# docker-deploy.sh: Deploy Docker container with health-check validation
set -euo pipefail
IFS=$'\n\t'

readonly IMAGE="${1:?Usage: $0 <image:tag> <container-name>}"
readonly CONTAINER_NAME="${2:?Usage: $0 <image:tag> <container-name>}"
readonly MAX_WAIT=120  # seconds to wait for health check
readonly HEALTH_INTERVAL=3

# Get current running container ID (if any)
get_running_id() {
  docker ps --filter "name=^/${CONTAINER_NAME}$" --format "{{.ID}}" 2>/dev/null || true
}

# Wait until container reaches healthy state
wait_for_healthy() {
  local container_id="$1"
  local elapsed=0
  echo "[INFO] Waiting for container $container_id to become healthy..."
  while (( elapsed < MAX_WAIT )); do
    local status
    status="$(docker inspect --format='{{.State.Health.Status}}' "$container_id" 2>/dev/null || echo "none")"
    case "$status" in
      healthy) echo "[OK] Container is healthy after ${elapsed}s"; return 0 ;;
      unhealthy) echo "[FAIL] Container is unhealthy" >&2; return 1 ;;
      none) echo "[INFO] No health check defined, checking if running..."
        local running
        running="$(docker inspect --format='{{.State.Running}}' "$container_id")"
        [[ "$running" == "true" ]] && return 0
        ;;
    esac
    sleep "$HEALTH_INTERVAL"
    (( elapsed += HEALTH_INTERVAL )) || true
  done
  echo "[FAIL] Timeout: container did not become healthy within ${MAX_WAIT}s" >&2
  return 1
}

old_id="$(get_running_id)"
echo "[INFO] Pulling image $IMAGE..."
docker pull "$IMAGE"

echo "[INFO] Starting new container $CONTAINER_NAME..."
docker run -d --name "${CONTAINER_NAME}_new" \
  --health-cmd="curl -sf http://localhost:80/health || exit 1" \
  --health-interval=5s \
  --health-timeout=3s \
  --health-retries=3 \
  "$IMAGE"

new_id="$(docker ps -q --filter "name=^/${CONTAINER_NAME}_new$")"
if wait_for_healthy "$new_id"; then
  [[ -n "$old_id" ]] && docker stop "$old_id" && docker rm "$old_id"
  docker rename "${CONTAINER_NAME}_new" "$CONTAINER_NAME"
  echo "[OK] Deployment successful"
else
  docker stop "$new_id" && docker rm "$new_id"
  echo "[FAIL] Deployment rolled back" >&2
  exit 1
fi

3. Monitoring container health checks with Bash

Health checks are the heart of any container automation. If you want to automate Docker and Kubernetes workflows with Bash, the script has to know when a container is really ready, not just when it was started. Docker provides built in health checks, defined either via the HEALTHCHECK directive in the Dockerfile or via --health-cmd on docker run. The status can be queried through docker inspect --format='{{.State.Health.Status}}' and takes the values starting, healthy, unhealthy or none.

Kubernetes offers three types of health checks. The livenessProbe checks whether a container is still alive and restarts it on failure. The readinessProbe checks whether a container is ready to receive traffic; pods without a passing readiness probe get no traffic from the service. The startupProbe gives slow starting applications more time. When automating Docker and Kubernetes workflows with Bash, the readiness probe is the most relevant one for rollout scripts: kubectl rollout status deployment/app implicitly waits for all pods to pass their readiness probe.

4. Image builds, tags and registry workflows

Image management is another area where automating Docker and Kubernetes workflows with Bash pays off significantly. Consistently tagging images, with the Git commit hash, a semantic version number and a latest alias, can be implemented as a Bash function and reused across every CI pipeline. The Git commit hash as an image tag is especially valuable because it uniquely and immutably ties each image to a code state. docker buildx build --platform linux/amd64,linux/arm64 for multi arch builds can be embedded into Bash workflows in the same way.

Registry cleanup scripts are a classic use case. Without automated cleanup a Docker registry grows uncontrollably. A Bash script that lists all tags of a repository, filters them by age or commit status and deletes stale tags prevents this problem. The Docker registry API is reachable via curl, and jq parses the JSON responses. When automating Docker and Kubernetes workflows with Bash, this cleanup script is typically a nightly cron job that ensures only production relevant images remain in the registry.


#!/usr/bin/env bash
# image-build-push.sh: Build, tag and push Docker image with consistent naming
set -euo pipefail

readonly REGISTRY="${REGISTRY:-registry.mironsoft.de}"
readonly APP_NAME="${APP_NAME:?APP_NAME must be set}"
readonly GIT_SHA="$(git rev-parse --short HEAD)"
readonly GIT_BRANCH="$(git rev-parse --abbrev-ref HEAD | tr '/' '-')"
readonly BUILD_DATE="$(date -u +%Y%m%d)"

# Determine version tag
if git describe --exact-match HEAD 2>/dev/null; then
  VERSION_TAG="$(git describe --exact-match HEAD)"
else
  VERSION_TAG="${GIT_BRANCH}-${GIT_SHA}"
fi

readonly FULL_IMAGE="${REGISTRY}/${APP_NAME}"
readonly TAGS=(
  "${FULL_IMAGE}:${VERSION_TAG}"
  "${FULL_IMAGE}:${GIT_SHA}"
  "${FULL_IMAGE}:${GIT_BRANCH}-latest"
)

# Build once, tag multiple times
echo "[INFO] Building ${FULL_IMAGE}:${VERSION_TAG}..."
docker build \
  --build-arg BUILD_DATE="$BUILD_DATE" \
  --build-arg GIT_SHA="$GIT_SHA" \
  --build-arg VERSION="$VERSION_TAG" \
  --label "org.opencontainers.image.revision=$GIT_SHA" \
  --label "org.opencontainers.image.created=$BUILD_DATE" \
  -t "${TAGS[0]}" .

# Tag all variants from the same build
for tag in "${TAGS[@]:1}"; do
  docker tag "${TAGS[0]}" "$tag"
  echo "[INFO] Tagged: $tag"
done

# Push all tags
for tag in "${TAGS[@]}"; do
  docker push "$tag"
  echo "[OK] Pushed: $tag"
done

echo "[OK] Build complete: $VERSION_TAG ($GIT_SHA)"

5. kubectl in Bash rollout scripts

Automating Docker and Kubernetes workflows with Bash using kubectl starts with choosing the right output format. kubectl get pods -o jsonpath='{.items[*].status.phase}' prints the status of every pod in the current namespace, and it is far more reliable to parse than the plain text output of kubectl get pods. For more complex queries, kubectl get deployment app -o json | jq '.status.availableReplicas' is the tool of choice. jq is available in nearly every CI environment and processes the JSON output of kubectl precisely.

When automating Docker and Kubernetes workflows with Bash, managing kubeconfig contexts is a common challenge in multi cluster environments. A Bash script that checks the active context before a deployment, switches to a temporary context and resets it again afterward prevents accidental deployments to the wrong cluster. The KUBECONFIG variable can point to a temporary configuration file that is only valid for the duration of the script.


#!/usr/bin/env bash
# k8s-rollout.sh: Kubernetes rolling deployment with validation
set -euo pipefail

readonly NAMESPACE="${K8S_NAMESPACE:?K8S_NAMESPACE must be set}"
readonly DEPLOYMENT="${K8S_DEPLOYMENT:?K8S_DEPLOYMENT must be set}"
readonly IMAGE="${NEW_IMAGE:?NEW_IMAGE must be set}"
readonly ROLLOUT_TIMEOUT="${ROLLOUT_TIMEOUT:-300s}"
readonly CONTAINER_NAME="${CONTAINER_NAME:-app}"

# Validate kubectl context before proceeding
validate_context() {
  local current_context
  current_context="$(kubectl config current-context)"
  echo "[INFO] Deploying to Kubernetes context: $current_context"
  echo "[INFO] Namespace: $NAMESPACE, Deployment: $DEPLOYMENT"
  read -r -p "Proceed? [y/N] " confirm
  [[ "${confirm,,}" == "y" ]] || { echo "Aborted."; exit 0; }
}

# Get current image for rollback
get_current_image() {
  kubectl get deployment "$DEPLOYMENT" \
    -n "$NAMESPACE" \
    -o jsonpath="{.spec.template.spec.containers[?(@.name=='${CONTAINER_NAME}')].image}"
}

# Check all pods in deployment are ready
all_pods_ready() {
  local desired available
  desired="$(kubectl get deployment "$DEPLOYMENT" -n "$NAMESPACE" \
    -o jsonpath='{.spec.replicas}')"
  available="$(kubectl get deployment "$DEPLOYMENT" -n "$NAMESPACE" \
    -o jsonpath='{.status.availableReplicas}' 2>/dev/null || echo 0)"
  [[ "$available" == "$desired" ]]
}

[[ -t 0 ]] && validate_context

old_image="$(get_current_image)"
echo "[INFO] Current image: $old_image"
echo "[INFO] New image: $IMAGE"

# Trigger rolling update
kubectl set image deployment/"$DEPLOYMENT" \
  "${CONTAINER_NAME}=${IMAGE}" \
  -n "$NAMESPACE"

echo "[INFO] Waiting for rollout to complete (timeout: $ROLLOUT_TIMEOUT)..."
if ! kubectl rollout status deployment/"$DEPLOYMENT" \
    -n "$NAMESPACE" \
    --timeout="$ROLLOUT_TIMEOUT"; then
  echo "[FAIL] Rollout failed, triggering undo..." >&2
  kubectl rollout undo deployment/"$DEPLOYMENT" -n "$NAMESPACE"
  echo "[INFO] Rolled back to: $old_image"
  exit 1
fi

all_pods_ready && echo "[OK] All pods are ready"
echo "[OK] Deployment successful: $IMAGE"

6. Zero downtime rollouts with wait logic

Zero downtime rollouts are the most demanding part of automating Docker and Kubernetes workflows with Bash. Kubernetes implements rolling updates by design, new pods start before old ones are terminated. But the wait logic in the Bash script must make sure the rollout is truly complete and every pod is healthy before downstream steps run. kubectl rollout status blocks until success or timeout and is the simplest implementation of this wait logic.

For finer grained control when automating Docker and Kubernetes workflows with Bash, it is worth writing a custom wait loop that not only checks rollout status but also actively detects pods stuck in CrashLoopBackOff or OOMKilled and immediately triggers a rollback, without waiting for the timeout. kubectl get events --field-selector reason=BackOff -n $NAMESPACE returns BackOff events in real time, and a Bash loop that watches these events while simultaneously waiting for rollout completion reacts faster to broken deployments than the default timeout behavior.

7. Automatic rollback on a failed rollout

Automatic rollback is a critical safety net when automating Docker and Kubernetes workflows with Bash. Kubernetes stores recent rollout revisions and lets you trigger an immediate rollback to the last known good version with kubectl rollout undo deployment/app. A Bash script combines this command with rollout monitoring: if the rollout fails within the timeout, kubectl rollout undo runs automatically and the script returns exit code 1, which flags the CI pipeline as a failed deployment.

Something to keep in mind when automating Docker and Kubernetes workflows with Bash with rollback logic: the rollback itself must also be monitored. A failed rollback, because the previous image is no longer available in the registry or because the old deployment was also crash looping, is a critical state that must be escalated immediately. A complete Bash script therefore implements a rollback timeout and sends a notification on failure before exiting with exit code 2.

8. Monitoring Kubernetes resources and pod status

Beyond individual deployments, automating Docker and Kubernetes workflows with Bash also covers cluster wide resource monitoring. kubectl top nodes and kubectl top pods report CPU and memory usage that Bash scripts can check against defined thresholds. A cron job that collects these metrics and sends a notification when critical limits are exceeded is a simple but effective addition to full monitoring solutions. For environments without Prometheus or Datadog, this Bash based approach is often the most pragmatic solution.

Namespace snapshots, a complete JSON dump of all Kubernetes resources in a namespace at a given point in time, are another useful workflow for automating Docker and Kubernetes workflows with Bash. A snapshot of the current state is saved before every deployment. After a failed rollback, the snapshot can be analyzed to reconstruct the state before the problem occurred. These snapshots are also valuable for post mortem analysis.

9. Docker vs. Kubernetes automation compared

When automating Docker and Kubernetes workflows with Bash, the patterns differ significantly depending on the target platform. This table shows the key differences.

Aspect Docker (Standalone) Kubernetes (kubectl) Bash Approach
Health check status docker inspect --format kubectl get pod -o jsonpath Wait loop with timeout and CrashLoop detection
Rollback Restart the old image kubectl rollout undo Automatic on exit code != 0, monitor the rollback too
Image tag strategy Registry chosen freely Respect ImagePullPolicy Git SHA plus semver tag, never latest in production
Context management DOCKER_HOST variable KUBECONFIG plus context Temporary kubeconfig, validate context before deploy
Parallelism Multiple containers by hand ReplicaSet manages pods Bash parallelization for multi service deployments

The table shows that although Docker and Kubernetes have different CLIs, Bash automation follows the same underlying patterns in both. Wait loops with timeouts, automatic rollback on failure, context validation before deployment and structured logging matter equally in both environments. Once you implement these patterns as reusable Bash functions, you can plug them into either type of deployment.

Mironsoft

Docker and Kubernetes automation, DevOps tooling and deployment infrastructure

Want to automate your Docker and Kubernetes workflows reliably?

We build health check monitoring, zero downtime rollouts and automatic rollback logic as robust Bash scripts for your Docker and Kubernetes environment: CI/CD platform independent and fully logged.

Rollout scripts

Zero downtime deployments with health check validation and automatic rollback

Image management

Build, tag, push and registry cleanup as one consistent Bash workflow

Cluster monitoring

Pod status, resource usage and event monitoring as Bash cron jobs

10. Summary

Anyone who wants to automate Docker and Kubernetes workflows with Bash does not need complex CI/CD platform features, they need clear patterns: wait loops for health checks, automatic rollback on failure, structured logging with exit codes and consistent image tagging with Git SHAs. These patterns can be implemented as Bash functions, reused freely and work in any CI environment.

The decisive advantage: Bash rollout scripts abstract away the complexity of the Docker CLI and kubectl behind a single, unified interface. New team members do not need to know the details of both CLIs, they can look at the rollout script and immediately understand which steps it runs, which failure conditions it catches and how it reacts to problems. That is maintainable, documented deployment automation.

Automating Docker and Kubernetes with Bash: the essentials at a glance

Health check wait loop

Never continue straight after docker run or kubectl apply. Actively wait for a healthy status or a passing readiness probe.

Automatic rollback

kubectl rollout undo on exit code != 0. Monitor the rollback itself too and protect it with a timeout.

Image tagging

Git SHA plus semver tag. Never latest in production, latest is not immutable and makes rollbacks unreliable.

Context validation

Explicitly check and confirm the Kubernetes context before every deployment. Accidental deployments to the wrong cluster happen more often than you would expect.

11. FAQ: Automating Docker and Kubernetes Workflows with Bash

1Why Bash instead of a deployment tool for Docker/Kubernetes?
Bash scripts are CI platform independent, run everywhere and create no tool lock in. Often easier to maintain than Helm or Ansible for smaller teams.
2Waiting for a healthy Docker container?
Poll docker inspect --format='{{.State.Health.Status}}' in a loop. Continue on healthy, fail immediately on unhealthy, exit code 1 after timeout.
3kubectl rollout status timeout?
--timeout=300s waits a maximum of 5 minutes, then exit code 1. With set -e that automatically triggers rollback logic.
4Detecting CrashLoopBackOff in Bash?
kubectl get pods -o jsonpath='{.items[*].status.containerStatuses[*].state.waiting.reason}' and check for CrashLoopBackOff.
5Why never 'latest' in production?
latest is not immutable. kubectl rollout undo does not work reliably. A Git SHA makes every deploy unique and rollback capable.
6Managing multiple Kubernetes contexts safely?
Point KUBECONFIG at a temporary file. Print and confirm the context explicitly before deploying. Inject from secrets in CI.
7Checking the registry API with Bash for stale images?
Docker Registry API v2 via curl: GET /v2/name/tags/list. Filter with jq, DELETE /v2/name/manifests/digest for old tags. Basic auth via the Authorization header.
8Building multi arch images with Bash?
docker buildx build --platform linux/amd64,linux/arm64 --push -t registry/app:tag . Enable buildx as the active builder with docker buildx create --use.
9Kubernetes snapshots before a deployment?
kubectl get all -n $NAMESPACE -o json > snapshot-date.json. Version in S3 or Git for post mortem analysis.
10Integrating rollout scripts into GitHub Actions?
Run directly as a shell step: run: ./scripts/deploy.sh. KUBECONFIG from secrets as an environment variable. Exit code ends the workflow step automatically.