Skip to content

Instantly share code, notes, and snippets.

@jg3
Created January 27, 2026 17:57
Show Gist options
  • Select an option

  • Save jg3/39581f6ab5fe5b8c5fcb01ff73c7f9f3 to your computer and use it in GitHub Desktop.

Select an option

Save jg3/39581f6ab5fe5b8c5fcb01ff73c7f9f3 to your computer and use it in GitHub Desktop.
An AI-generated script to gather troubleshooting information, compose a prompt, and provide a paste-able chat analysis for human consumption.
#!/bin/bash
# docker-status.sh: Gathers Docker and system status, provides a chatbot-ready prompt.
# Enhanced: If yq is available, matches containers to Compose services with high confidence.
# Early sudo test
echo "Testing for sudo privileges..."
if sudo -v 2>/dev/null; then
SUDO_OK=1
echo "Sudo available."
else
SUDO_OK=0
echo "Warning: Sudo privileges not available. Some information may be incomplete."
fi
# yq detection
if command -v yq >/dev/null 2>&1; then
YQ_AVAILABLE=1
else
YQ_AVAILABLE=0
fi
# Try a command, escalate with sudo if needed
try_command() {
CMD="$*"
OUTPUT=$($CMD 2>&1)
if echo "$OUTPUT" | grep -qi "permission denied\|Got permission denied"; then
if [[ $SUDO_OK -eq 1 ]]; then
OUTPUT=$(sudo $CMD 2>&1)
fi
fi
echo "$OUTPUT"
}
# Stoplight emoji for status
container_status_light() {
local status="$1"
if [[ "$status" =~ ^Up ]]; then
echo "🟢"
elif [[ "$status" =~ ^Exited|Restarting ]]; then
echo "🟡"
else
echo "🔴"
fi
}
# Compose file finder (store path, project root, etc.)
COMPOSE_FILES=()
while IFS= read -r f; do
COMPOSE_FILES+=("$f")
done < <(find /home /opt /srv /var -type f \( -name 'docker-compose.yml' -o -name 'compose.yml' \) 2>/dev/null)
# Compose service extraction (if yq is available)
declare -A COMPOSE_SERVICES # Key: <compose file>::<service name> -> image
if [[ $YQ_AVAILABLE -eq 1 && ${#COMPOSE_FILES[@]} -gt 0 ]]; then
for f in "${COMPOSE_FILES[@]}"; do
services=$(yq eval '.services | keys | .[]' "$f" 2>/dev/null)
for svc in $services; do
image=$(yq eval ".services.\"$svc\".image" "$f" 2>/dev/null)
COMPOSE_SERVICES["$f::$svc"]="$image"
done
done
fi
# Gather all containers (running and stopped)
CONTAINER_IDS=($(try_command docker ps -a -q))
CONTAINER_TABLE=""
CONTAINER_DETAILS=""
CONTAINER_WARNINGS=""
if [[ ${#CONTAINER_IDS[@]} -eq 0 ]]; then
CONTAINER_TABLE="No containers found."
CONTAINER_DETAILS="No containers found."
else
CONTAINER_TABLE="| Name | Status | Uptime/Age | Startup Method |\n|------|--------|------------|---------------|"
for CID in "${CONTAINER_IDS[@]}"; do
if [[ ! "$CID" =~ ^[a-f0-9]{12,}$ ]]; then
continue
fi
INSPECT=$(try_command docker inspect "$CID" 2>/dev/null)
if [[ -z "$INSPECT" || "$INSPECT" =~ "Error: No such object" || "$INSPECT" =~ "no such object" ]]; then
CONTAINER_WARNINGS+="Warning: Skipping container $CID - could not inspect.\n"
continue
fi
CNAME=$(echo "$INSPECT" | grep -m1 '"Name":' | sed 's/.*"Name": "//;s/",//;s/\///')
STATUS=$(try_command docker ps -a --filter "id=$CID" --format '{{.Status}}')
[[ -z "$STATUS" ]] && STATUS="Unknown"
UPTIME_AGE=$(echo "$STATUS" | cut -d' ' -f2-)
[[ -z "$UPTIME_AGE" ]] && UPTIME_AGE="$STATUS"
LIGHT=$(container_status_light "$STATUS")
# Compose info and startup method
COMPOSE_PROJECT=$(echo "$INSPECT" | grep -A1 '"com.docker.compose.project"' | grep '"Value":' | sed 's/.*"Value": "//;s/",//')
COMPOSE_SERVICE=$(echo "$INSPECT" | grep -A1 '"com.docker.compose.service"' | grep '"Value":' | sed 's/.*"Value": "//;s/",//')
START_METHOD=""
if [[ -n "$COMPOSE_PROJECT" && -n "$COMPOSE_SERVICE" && $YQ_AVAILABLE -eq 1 ]]; then
# Try to find the matching compose file
MATCHED_FILE=""
MATCHED_IMAGE=""
for f in "${COMPOSE_FILES[@]}"; do
img="${COMPOSE_SERVICES["$f::$COMPOSE_SERVICE"]}"
if [[ -n "$img" ]]; then
MATCHED_FILE="$f"
MATCHED_IMAGE="$img"
break
fi
done
if [[ -n "$MATCHED_FILE" ]]; then
START_METHOD="Compose: $MATCHED_FILE (service: $COMPOSE_SERVICE)"
else
START_METHOD="Compose project: $COMPOSE_PROJECT (service: $COMPOSE_SERVICE, compose file not found/matched)"
fi
elif [[ -n "$COMPOSE_PROJECT" && $YQ_AVAILABLE -eq 0 ]]; then
START_METHOD="Compose project: $COMPOSE_PROJECT (service: $COMPOSE_SERVICE, yq not available for matching)"
else
START_CMD=$(try_command docker inspect --format='{{.Path}} {{range .Args}}{{.}} {{end}}' "$CID" 2>/dev/null)
[[ -z "$START_CMD" || "$START_CMD" =~ "no such object" || "$START_CMD" =~ "Error" ]] && START_CMD="(Could not determine start command)"
START_METHOD="$START_CMD"
fi
CONTAINER_TABLE+="\n| $CNAME | $LIGHT $STATUS | $UPTIME_AGE | $START_METHOD |"
done
# Prepare per-container detailed info
for CID in "${CONTAINER_IDS[@]}"; do
if [[ ! "$CID" =~ ^[a-f0-9]{12,}$ ]]; then
continue
fi
INSPECT=$(try_command docker inspect "$CID" 2>/dev/null)
if [[ -z "$INSPECT" || "$INSPECT" =~ "Error: No such object" || "$INSPECT" =~ "no such object" ]]; then
continue
fi
NAME=$(echo "$INSPECT" | grep -m1 '"Name":' | sed 's/.*"Name": "//;s/",//;s/\///')
IMAGE=$(echo "$INSPECT" | grep -m1 '"Image":' | sed 's/.*"Image": "//;s/",//')
STATE=$(echo "$INSPECT" | grep -m1 '"Status":' | sed 's/.*"Status": "//;s/",//')
RESTART_POLICY=$(echo "$INSPECT" | grep -m1 '"RestartPolicy":' -A 2 | grep '"Name":' | head -1 | sed 's/.*"Name": "//;s/",//')
# Entrypoint/command
ENTRY_CMD=$(try_command docker inspect --format='{{json .Config.Entrypoint}} {{json .Config.Cmd}}' "$CID" 2>/dev/null)
# Ports
PORTS=$(echo "$INSPECT" | grep -A10 '"Ports": {' | grep -v '"Ports": {' | sed -e 's/^[ \t]*//' | grep -v '^}$')
# Volumes
VOLUMES=$(echo "$INSPECT" | grep -A20 '"Mounts": \[' | grep '"Source":' | sed 's/.*"Source": "//;s/",//')
# Networks
NETWORKS=$(echo "$INSPECT" | grep '"NetworkID":' | sed 's/.*"NetworkID": "//;s/",//')
# Env vars
ENVS=$(echo "$INSPECT" | grep -A20 '"Env": \[' | grep -v '"Env": \[' | grep -v '],' | sed 's/^[ \t]*"//;s/",*$//')
# Compose matching
COMPOSE_PROJECT=$(echo "$INSPECT" | grep -A1 '"com.docker.compose.project"' | grep '"Value":' | sed 's/.*"Value": "//;s/",//')
COMPOSE_SERVICE=$(echo "$INSPECT" | grep -A1 '"com.docker.compose.service"' | grep '"Value":' | sed 's/.*"Value": "//;s/",//')
if [[ -n "$COMPOSE_PROJECT" && -n "$COMPOSE_SERVICE" && $YQ_AVAILABLE -eq 1 ]]; then
MATCHED_FILE=""
for f in "${COMPOSE_FILES[@]}"; do
img="${COMPOSE_SERVICES["$f::$COMPOSE_SERVICE"]}"
if [[ -n "$img" ]]; then
MATCHED_FILE="$f"
break
fi
done
if [[ -n "$MATCHED_FILE" ]]; then
COMPOSE_NOTE="This container is managed by Compose project '$COMPOSE_PROJECT', file: $MATCHED_FILE, service: $COMPOSE_SERVICE."
else
COMPOSE_NOTE="Compose project label found, but could not confidently match to a Compose file (service: $COMPOSE_SERVICE)."
fi
elif [[ -n "$COMPOSE_PROJECT" && $YQ_AVAILABLE -eq 0 ]]; then
COMPOSE_NOTE="Compose project label found, but yq is not available for detailed file/service matching."
else
COMPOSE_NOTE="No Compose label found; container appears to have been started manually."
fi
CONTAINER_DETAILS+="
Container: $NAME
Image: $IMAGE
State: $STATE
Entrypoint/Command: $ENTRY_CMD
Ports: $(echo "$PORTS" | tr '\n' '; ')
Volumes: $(echo "$VOLUMES" | tr '\n' '; ')
Networks: $(echo "$NETWORKS" | tr '\n' '; ')
Environment Variables: $(echo "$ENVS" | tr '\n' '; ')
Restart policy: $RESTART_POLICY
$COMPOSE_NOTE
"
done
fi
IMAGES=$(try_command docker images --format '{{.Repository}}:{{.Tag}} ({{.ID}})')
# Problems/Concerns/Recommendations
PROBLEMS=""
if [[ "$DOCKER_STATUS" != "active" ]]; then
PROBLEMS+="Docker service is not running (status: $DOCKER_STATUS).
"
fi
if ! command -v docker &>/dev/null; then
PROBLEMS+="The 'docker' command is not found in PATH.
"
fi
if [[ ${#CONTAINER_IDS[@]} -eq 0 ]]; then
PROBLEMS+="No containers found.
"
fi
if [[ -n "$CONTAINER_WARNINGS" ]]; then
PROBLEMS+="$CONTAINER_WARNINGS"
fi
# System load information
LOAD_AVG=$(cat /proc/loadavg | awk '{print $1, $2, $3}')
CPU_INFO=$(top -bn1 | grep "Cpu(s)" | awk '{print $2+$4+$6+$10+$12+$14 "% used"}')
MEM_INFO=$(free -h | awk '/^Mem:/ {print $3 " used / " $2 " total"}')
# Docker resource usage (with sudo if possible), only if containers exist
if [[ ${#CONTAINER_IDS[@]} -gt 0 ]]; then
if [[ $SUDO_OK -eq 1 ]]; then
DOCKER_TOP=$(sudo docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" 2>/dev/null)
else
DOCKER_TOP=$(docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" 2>/dev/null)
fi
if [[ -z "$DOCKER_TOP" || "$DOCKER_TOP" =~ "error" || "$DOCKER_TOP" =~ "Error" ]]; then
DOCKER_TOP="(docker stats failed: insufficient permissions or Docker daemon is not running.)"
fi
else
DOCKER_TOP="No running or stopped containers."
fi
# Recommendations
RECOMMENDATIONS=""
MEM_USED=$(echo "$MEM_INFO" | awk '{print $1}' | sed 's/[^0-9.]//g')
MEM_TOTAL=$(echo "$MEM_INFO" | awk '{print $3}' | sed 's/[^0-9.]//g')
if [[ -n "$MEM_USED" && -n "$MEM_TOTAL" && "$MEM_TOTAL" != "0" ]]; then
MEM_PCT=$(awk -v u="$MEM_USED" -v t="$MEM_TOTAL" 'BEGIN{if(t>0) print u/t; else print 0}')
if (( $(echo "$MEM_PCT > 0.8" | bc -l) )); then
RECOMMENDATIONS+="Memory usage is high. Investigate running containers and system processes.
"
fi
fi
if [[ $(echo "$LOAD_AVG" | awk '{print ($1 > 2.0)}') -eq 1 ]]; then
RECOMMENDATIONS+="System load is high. Consider stopping unnecessary containers or applications.
"
fi
# yq info note if not available
YQ_NOTE=""
if [[ $YQ_AVAILABLE -eq 0 ]]; then
YQ_NOTE="Note: Deeper analysis of Compose files and high-confidence matching of containers to Compose services is available if yq is installed and in your PATH."
fi
# Prepare the AI prompt output
AI_PROMPT=$(cat <<EOF
==============================================================================
INSTRUCTIONS:
Copy everything between the "==== cut here ====" lines and paste it as a prompt
to your preferred AI chatbot. The AI should explain the Docker environment,
what is running, how it started, what will happen at reboot, and give
recommendations or note any problems. Handy verification commands should be included.
==============================================================================
==== cut here ====
System Overview:
----------------
System load averages (1m, 5m, 15m): $LOAD_AVG
CPU usage: $CPU_INFO
Memory usage: $MEM_INFO
Docker Engine:
--------------
Service status: $DOCKER_STATUS
Docker version: $DOCKER_VERSION
Service details:
$DOCKER_SERVICE_STATUS
Docker Compose Deployments:
---------------------------
$(if [[ ${#COMPOSE_FILES[@]} -gt 0 ]]; then for f in "${COMPOSE_FILES[@]}"; do echo "$f"; done; else echo "No Compose files found."; fi)
$YQ_NOTE
All Docker Images:
------------------
$IMAGES
All Containers (Summary Table):
-------------------------------
$CONTAINER_TABLE
All Containers (Detailed):
--------------------------
$CONTAINER_DETAILS
Docker resource usage (from 'docker stats'):
-------------------------------------------
$DOCKER_TOP
Problems detected:
------------------
$PROBLEMS
Potential concerns & recommendations:
-------------------------------------
$RECOMMENDATIONS
Handy command-line verifications:
---------------------------------
- Check Docker service status: systemctl status docker
- List all containers: docker ps -a
- List running containers: docker ps
- Show running Compose apps: docker compose ps
- Show resource usage: docker stats
- Inspect a container: docker inspect <container_id>
- Show load/memory: top, free -h, cat /proc/loadavg
==== cut here ====
EOF
)
# Output interaction: file or screen
USERNAME=$(whoami)
HOSTNAME=$(hostname -s)
OUTFILE="/tmp/docker_ai_prompt_${USERNAME}@${HOSTNAME}.txt"
while true; do
echo "How would you like to handle the output?"
echo " [f] Write to file ($OUTFILE)"
echo " [s] Show on screen"
read -rp "Choose [f/s]: " RESP
if [[ "$RESP" =~ ^[Ff]$ ]]; then
echo "$AI_PROMPT" > "$OUTFILE"
echo "Prompt saved to $OUTFILE"
echo
echo "To copy this prompt directly into your Mac's clipboard, run the following command on your Mac:"
echo
echo "ssh $USERNAME@$HOSTNAME cat $OUTFILE | pbcopy"
echo
break
elif [[ "$RESP" =~ ^[Ss]$ ]]; then
echo "$AI_PROMPT"
break
else
echo "Please enter 'f' to write to file or 's' to display on screen."
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment