#!/usr/bin/env bash # # TrueNAS Apps & Docker Collection Script # Run this directly on your TrueNAS server to collect app/container information # # Usage: # 1. Copy this script to your TrueNAS server # 2. Run: bash collect-truenas-apps.sh # 3. Transfer the generated tar.gz back to your workstation # set -euo pipefail TIMESTAMP="$(date +%Y%m%d-%H%M%S)" OUTPUT_DIR="/tmp/truenas-apps-export-${TIMESTAMP}" # Colors GREEN='\033[0;32m' CYAN='\033[0;36m' NC='\033[0m' echo -e "${CYAN}=====================================${NC}" echo -e "${CYAN}TrueNAS Apps & Docker Collection${NC}" echo -e "${CYAN}=====================================${NC}" echo # Create directory structure mkdir -p "$OUTPUT_DIR"/{configs/apps,exports/apps} # Collect Docker information echo -e "${CYAN}=== Docker Containers ===${NC}" if command -v docker &>/dev/null; then # All containers (convert to JSON array) if sudo docker ps -a --format '{{json .}}' 2>/dev/null | jq -s '.' 2>/dev/null > "$OUTPUT_DIR/exports/apps/docker-containers.json" || \ sudo docker ps -a --format '{{json .}}' 2>/dev/null | sed '1s/^/[/; $!s/$/,/; $s/$/]/' > "$OUTPUT_DIR/exports/apps/docker-containers.json"; then [[ -s "$OUTPUT_DIR/exports/apps/docker-containers.json" ]] && echo -e "${GREEN}✓${NC} Docker containers (JSON format)" fi # Human-readable format if sudo docker ps -a > "$OUTPUT_DIR/exports/apps/docker-containers.txt" 2>/dev/null; then [[ -s "$OUTPUT_DIR/exports/apps/docker-containers.txt" ]] && echo -e "${GREEN}✓${NC} Docker containers (text format)" fi # Docker images if sudo docker images --format '{{json .}}' 2>/dev/null | jq -s '.' 2>/dev/null > "$OUTPUT_DIR/exports/apps/docker-images.json" || \ sudo docker images --format '{{json .}}' 2>/dev/null | sed '1s/^/[/; $!s/$/,/; $s/$/]/' > "$OUTPUT_DIR/exports/apps/docker-images.json"; then [[ -s "$OUTPUT_DIR/exports/apps/docker-images.json" ]] && echo -e "${GREEN}✓${NC} Docker images" fi # Docker networks if sudo docker network ls --format '{{json .}}' 2>/dev/null | jq -s '.' 2>/dev/null > "$OUTPUT_DIR/exports/apps/docker-networks.json" || \ sudo docker network ls --format '{{json .}}' 2>/dev/null | sed '1s/^/[/; $!s/$/,/; $s/$/]/' > "$OUTPUT_DIR/exports/apps/docker-networks.json"; then [[ -s "$OUTPUT_DIR/exports/apps/docker-networks.json" ]] && echo -e "${GREEN}✓${NC} Docker networks" fi # Docker volumes if sudo docker volume ls --format '{{json .}}' 2>/dev/null | jq -s '.' 2>/dev/null > "$OUTPUT_DIR/exports/apps/docker-volumes.json" || \ sudo docker volume ls --format '{{json .}}' 2>/dev/null | sed '1s/^/[/; $!s/$/,/; $s/$/]/' > "$OUTPUT_DIR/exports/apps/docker-volumes.json"; then [[ -s "$OUTPUT_DIR/exports/apps/docker-volumes.json" ]] && echo -e "${GREEN}✓${NC} Docker volumes" fi # Docker compose projects (if available) if command -v docker-compose &>/dev/null || docker compose version &>/dev/null 2>&1; then if sudo docker compose ls --format '{{json .}}' 2>/dev/null | jq -s '.' 2>/dev/null > "$OUTPUT_DIR/exports/apps/docker-compose-projects.json" || \ sudo docker compose ls --format '{{json .}}' 2>/dev/null | sed '1s/^/[/; $!s/$/,/; $s/$/]/' > "$OUTPUT_DIR/exports/apps/docker-compose-projects.json"; then [[ -s "$OUTPUT_DIR/exports/apps/docker-compose-projects.json" ]] && echo -e "${GREEN}✓${NC} Docker Compose projects" || \ echo " ⊘ No Docker Compose projects found" fi fi else echo " ⊘ Docker command not available" fi echo # Collect TrueNAS app metadata echo -e "${CYAN}=== TrueNAS Apps ===${NC}" if [[ -d /mnt/.ix-apps ]]; then # App metadata if [[ -f /mnt/.ix-apps/metadata.yaml ]]; then sudo cp /mnt/.ix-apps/metadata.yaml "$OUTPUT_DIR/configs/apps/metadata.yaml" && \ echo -e "${GREEN}✓${NC} App metadata" fi # User config if [[ -f /mnt/.ix-apps/user_config.yaml ]]; then sudo cp /mnt/.ix-apps/user_config.yaml "$OUTPUT_DIR/configs/apps/user_config.yaml" && \ echo -e "${GREEN}✓${NC} User config" fi # App configs directory listing if [[ -d /mnt/.ix-apps/app_configs ]]; then sudo ls -laR /mnt/.ix-apps/app_configs/ > "$OUTPUT_DIR/exports/apps/app_configs_list.txt" 2>/dev/null && \ echo -e "${GREEN}✓${NC} App configs listing" fi # App mounts listing if [[ -d /mnt/.ix-apps/app_mounts ]]; then sudo ls -laR /mnt/.ix-apps/app_mounts/ > "$OUTPUT_DIR/exports/apps/app_mounts_list.txt" 2>/dev/null && \ echo -e "${GREEN}✓${NC} App mounts listing" fi # Docker directory info if [[ -d /mnt/.ix-apps/docker ]]; then sudo du -sh /mnt/.ix-apps/docker/* 2>/dev/null > "$OUTPUT_DIR/exports/apps/docker_sizes.txt" && \ echo -e "${GREEN}✓${NC} Docker storage sizes" fi else echo " ⊘ TrueNAS apps directory not found" fi echo # Collect individual container details echo -e "${CYAN}=== Container Details ===${NC}" mkdir -p "$OUTPUT_DIR/exports/apps/containers" if command -v docker &>/dev/null; then CONTAINER_COUNT=0 while IFS= read -r container_id; do CONTAINER_NAME=$(sudo docker inspect "$container_id" --format '{{.Name}}' | sed 's/^\///') sudo docker inspect "$container_id" > "$OUTPUT_DIR/exports/apps/containers/${CONTAINER_NAME}.json" 2>/dev/null && \ ((CONTAINER_COUNT++)) || true done < <(sudo docker ps -aq) echo -e "${GREEN}✓${NC} Collected details for $CONTAINER_COUNT containers" else echo " ⊘ Docker command not available" fi echo # Collect container logs (last 500 lines, with size limit) echo -e "${CYAN}=== Container Logs ===${NC}" mkdir -p "$OUTPUT_DIR/exports/apps/logs" if command -v docker &>/dev/null; then LOG_COUNT=0 while IFS= read -r container_id; do CONTAINER_NAME=$(sudo docker inspect "$container_id" --format '{{.Name}}' 2>/dev/null | sed 's/^\//') if [[ -n "$CONTAINER_NAME" ]]; then # Collect last 500 lines of logs (prevents huge files) sudo docker logs --tail 500 "$container_id" > "$OUTPUT_DIR/exports/apps/logs/${CONTAINER_NAME}.log" 2>&1 && \ ((LOG_COUNT++)) || true fi done < <(sudo docker ps -aq) echo -e "${GREEN}✓${NC} Collected logs for $LOG_COUNT containers" else echo " ⊘ Docker command not available" fi echo # Collect Docker Compose files from running containers echo -e "${CYAN}=== Docker Compose Files ===${NC}" mkdir -p "$OUTPUT_DIR/configs/apps/compose" if command -v docker &>/dev/null; then COMPOSE_COUNT=0 while IFS= read -r container_id; do CONTAINER_NAME=$(sudo docker inspect "$container_id" --format '{{.Name}}' 2>/dev/null | sed 's/^\//') COMPOSE_FILE=$(sudo docker inspect "$container_id" --format '{{index .Config.Labels "com.docker.compose.project.config_files"}}' 2>/dev/null) if [[ -n "$COMPOSE_FILE" ]] && [[ -f "$COMPOSE_FILE" ]]; then PROJECT_NAME=$(sudo docker inspect "$container_id" --format '{{index .Config.Labels "com.docker.compose.project"}}' 2>/dev/null) sudo cp "$COMPOSE_FILE" "$OUTPUT_DIR/configs/apps/compose/${PROJECT_NAME:-$CONTAINER_NAME}.yml" 2>/dev/null && \ ((COMPOSE_COUNT++)) || true fi done < <(sudo docker ps -aq) if [[ $COMPOSE_COUNT -gt 0 ]]; then echo -e "${GREEN}✓${NC} Collected $COMPOSE_COUNT Docker Compose files" else echo " ⊘ No Docker Compose files found" fi else echo " ⊘ Docker command not available" fi echo # Collect individual app configurations from app_configs echo -e "${CYAN}=== Individual App Configs ===${NC}" mkdir -p "$OUTPUT_DIR/configs/apps/app_configs" if [[ -d /mnt/.ix-apps/app_configs ]]; then APP_COUNT=0 for app_dir in /mnt/.ix-apps/app_configs/*; do if [[ -d "$app_dir" ]]; then APP_NAME=$(basename "$app_dir") mkdir -p "$OUTPUT_DIR/configs/apps/app_configs/$APP_NAME" # Copy configuration files (YAML, JSON, ENV) sudo find "$app_dir" -maxdepth 2 -type f \( -name "*.yaml" -o -name "*.yml" -o -name "*.json" -o -name "*.env" -o -name "*.conf" \) \ -exec cp {} "$OUTPUT_DIR/configs/apps/app_configs/$APP_NAME/" \; 2>/dev/null && \ ((APP_COUNT++)) || true fi done if [[ $APP_COUNT -gt 0 ]]; then echo -e "${GREEN}✓${NC} Collected configs for $APP_COUNT apps" else echo " ⊘ No app configs found" fi else echo " ⊘ TrueNAS app_configs directory not found" fi echo # Generate summary cat > "$OUTPUT_DIR/SUMMARY.md" << EOF # TrueNAS Apps & Docker Export Summary **Date**: $(date '+%Y-%m-%d %H:%M:%S') **Host**: $(hostname) ## Docker Containers $(if command -v docker &>/dev/null; then echo "- Running: $(sudo docker ps -q | wc -l)" echo "- Total: $(sudo docker ps -aq | wc -l)" echo "" echo "### Container List" echo "" sudo docker ps -a --format "- {{.Names}} ({{.Image}}) - {{.Status}}" else echo "Docker not available" fi) ## TrueNAS Apps $(if [[ -d /mnt/.ix-apps ]]; then echo "- Apps directory: /mnt/.ix-apps" echo "- App configs: $(sudo ls /mnt/.ix-apps/app_configs 2>/dev/null | wc -l) apps" else echo "TrueNAS apps directory not found" fi) ## Files Collected \`\`\` $(find "$OUTPUT_DIR" -type f | sed "s|$OUTPUT_DIR/||" | sort) \`\`\` EOF # Create compressed archive echo -e "${CYAN}=== Creating Archive ===${NC}" ARCHIVE="/tmp/truenas-apps-export-${TIMESTAMP}.tar.gz" tar -czf "$ARCHIVE" -C /tmp "$(basename "$OUTPUT_DIR")" 2>/dev/null echo -e "${GREEN}✓${NC} Archive created: $ARCHIVE" echo -e "${GREEN}✓${NC} Size: $(du -h "$ARCHIVE" | cut -f1)" echo echo -e "${CYAN}=====================================${NC}" echo -e "${CYAN}Collection Complete!${NC}" echo -e "${CYAN}=====================================${NC}" echo echo "Output directory: $OUTPUT_DIR" echo "Archive: $ARCHIVE" echo echo "Next steps:" echo "1. Download the archive to your workstation:" echo " scp truenas_admin@192.168.2.150:$ARCHIVE /home/jramos/truenas/disaster-recovery/" echo echo "2. Extract to your disaster-recovery folder:" echo " cd /home/jramos/truenas/disaster-recovery" echo " tar -xzf $(basename "$ARCHIVE")" echo