Skip to content

Instantly share code, notes, and snippets.

@robertsinfosec
Last active December 5, 2025 01:41
Show Gist options
  • Select an option

  • Save robertsinfosec/8dccc6ebaf629346a5701b3677eb2f91 to your computer and use it in GitHub Desktop.

Select an option

Save robertsinfosec/8dccc6ebaf629346a5701b3677eb2f91 to your computer and use it in GitHub Desktop.
Simple script to upgrade any Docker Compose services that use the `:latest` tag to update that service to the latest version of that container image. This could be run weekly, monthly or ad-hoc to make sure you are on the latest container image build. Consider putting this in `/usr/local/bin/` and `chmod +x compose-upgrade.sh` to mark it as exec…
#!/usr/bin/env bash
set -euo pipefail
# compose-upgrade.sh
# Usage: compose-upgrade.sh [--help] [--service-name NAME] [--service-path PATH] [--log-dir PATH]
#
# Runs: docker compose pull && docker compose up -d --remove-orphans
# Prints progress with [+]/[-] markers, logs to file, and exits nonzero on failure.
show_help() {
cat <<'EOF'
Usage: compose-upgrade.sh [OPTIONS]
Options:
--help Show this help and exit
--service-name NAME Short name used in log filename (default: myservice)
--service-path PATH Path to compose project dir containing docker-compose.yml (default: /path/to/your/composedir)
--log-dir PATH Directory to write logs (default: /var/log)
Examples:
compose-upgrade.sh --service-name web --service-path /srv/web
EOF
}
# Defaults (editable)
SERVICE_NAME="tbd-service-change-me"
SERVICE_PATH="/opt/path-to-docker-compose"
LOG_DIR="/var/log"
# Parse args (simple)
while [ $# -gt 0 ]; do
case "$1" in
--help) show_help; exit 0 ;;
--service-name) shift; SERVICE_NAME="${1:-}"; ;;
--service-path) shift; SERVICE_PATH="${1:-}"; ;;
--log-dir) shift; LOG_DIR="${1:-}"; ;;
*) echo "Unknown arg: $1"; show_help; exit 2 ;;
esac
shift
done
LOG_FILE="${LOG_DIR%/}/compose-upgrade-${SERVICE_NAME}.log"
DOCKER_BIN="$(command -v docker || true)"
timestamp() { date -u +"%Y-%m-%d %H:%M:%S UTC"; }
# Format elapsed seconds to ddd.HH:MM:ss
format_elapsed() {
local secs=$1
local days=$((secs/86400)); secs=$((secs%86400))
local hours=$((secs/3600)); secs=$((secs%3600))
local mins=$((secs/60)); local s=$((secs%60))
printf "%03d.%02d:%02d:%02d" "$days" "$hours" "$mins" "$s"
}
# Ensure log dir exists and writable
mkdir -p "$LOG_DIR"
if ! touch "$LOG_FILE" 2>/dev/null; then
echo "[-] Error: cannot write to log file $LOG_FILE" >&2
exit 1
fi
start_ts=$(date +%s)
echo "=== $(timestamp) Starting compose upgrade for ${SERVICE_NAME} ===" >>"$LOG_FILE"
echo "[*] Start: $(timestamp) for ${SERVICE_NAME}"
fail() {
local step="$1"; local msg="$2"
echo "[-] Error: ${step} failed" | tee -a "$LOG_FILE" >&2
if [ -n "$msg" ]; then
echo "[-] Message: $msg" | tee -a "$LOG_FILE" >&2
fi
echo "=== $(timestamp) FAILED compose upgrade for ${SERVICE_NAME} ===" >>"$LOG_FILE"
end_ts=$(date +%s)
elapsed=$((end_ts-start_ts))
echo "=== Elapsed: $(format_elapsed "$elapsed") ===" >>"$LOG_FILE"
echo "[-] Elapsed: $(format_elapsed "$elapsed")"
exit 1
}
succeed() {
echo "[+] Success: $1"
echo "=== $(timestamp) Completed compose upgrade for ${SERVICE_NAME} ===" >>"$LOG_FILE"
end_ts=$(date +%s)
elapsed=$((end_ts-start_ts))
echo "=== Elapsed: $(format_elapsed "$elapsed") ===" >>"$LOG_FILE"
echo "[+] Elapsed: $(format_elapsed "$elapsed")"
exit 0
}
# Check docker binary
if [ -z "$DOCKER_BIN" ]; then
fail "Preflight" "docker binary not found in PATH"
fi
# Ensure SERVICE_PATH exists
if [ ! -d "$SERVICE_PATH" ]; then
fail "Preflight" "service path does not exist: $SERVICE_PATH"
fi
# Change to service dir
echo "[*] Attempting to cd to ${SERVICE_PATH}..."
if ! cd "$SERVICE_PATH" 2>&1 >>"$LOG_FILE"; then
fail "cd" "Cannot cd to ${SERVICE_PATH}"
fi
echo "[+] Success: cd to ${SERVICE_PATH}"
# Step 1: pull
echo "[*] Attempting to docker compose pull..."
if ! "$DOCKER_BIN" compose pull >>"$LOG_FILE" 2>&1; then
# capture last 200 lines of log to show as message
msg="$(tail -n 200 "$LOG_FILE")"
fail "docker compose pull" "$msg"
fi
echo "[+] Success: docker compose pull completed"
# Step 2: up -d --remove-orphans
echo "[*] Attempting to docker compose up -d --remove-orphans..."
if ! "$DOCKER_BIN" compose up -d --remove-orphans >>"$LOG_FILE" 2>&1; then
msg="$(tail -n 200 "$LOG_FILE")"
fail "docker compose up" "$msg"
fi
echo "[+] Success: docker compose up completed"
# All done
succeed "All steps completed successfully"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment