Created
January 27, 2026 17:57
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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