#!/bin/bash # Update NPMplus to latest release (2026-01-20-r2) # Creates backup before upgrading as recommended set -euo pipefail CONTAINER_ID="10233" NODE="r630-01" DOCKER_CONTAINER="npmplus" CURRENT_IMAGE="zoeyvid/npmplus:latest" NEW_IMAGE="zoeyvid/npmplus:2026-01-20-r2" BACKUP_DIR="/data/npmplus-backups" TIMESTAMP=$(date +%Y%m%d_%H%M%S) # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' echo "==========================================" echo "Update NPMplus to 2026-01-20-r2" echo "==========================================" echo "" echo "Current image: $CURRENT_IMAGE" echo "New image: $NEW_IMAGE" echo "" # Step 1: Check current version echo -e "${BLUE}Step 1: Checking current NPMplus version...${NC}" CURRENT_VERSION=$(ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'pct exec ${CONTAINER_ID} -- docker inspect ${DOCKER_CONTAINER} --format \"{{.Config.Image}}\" 2>&1'" 2>&1) echo "Current: $CURRENT_VERSION" if echo "$CURRENT_VERSION" | grep -q "2026-01-20-r2"; then echo -e "${GREEN}✅ Already running 2026-01-20-r2${NC}" exit 0 fi # Step 2: Create backup echo -e "${BLUE}Step 2: Creating backup...${NC}" echo "⚠️ IMPORTANT: Creating backup before upgrade (as recommended)" # Create backup directory ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'mkdir -p ${BACKUP_DIR} 2>&1'" 2>&1 # Backup data volume echo "Backing up NPMplus data..." # Get actual data volume path DATA_VOLUME=$(ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'pct exec ${CONTAINER_ID} -- docker inspect ${DOCKER_CONTAINER} --format \"{{range .Mounts}}{{if eq .Destination \\\"/data\\\"}}{{.Source}}{{end}}{{end}}\" 2>&1'" 2>&1 | head -1) if [ -z "$DATA_VOLUME" ]; then DATA_VOLUME="/data/npmplus" fi echo "Data volume path: $DATA_VOLUME" # Create backup from inside container (data is in container's /data) BACKUP_FILE="${BACKUP_DIR}/npmplus-backup-${TIMESTAMP}.tar.gz" echo "Creating backup from container..." BACKUP_RESULT=$(ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'pct exec ${CONTAINER_ID} -- docker exec ${DOCKER_CONTAINER} tar -czf /tmp/npmplus-backup.tar.gz -C /data . 2>&1 && \ pct exec ${CONTAINER_ID} -- docker cp ${DOCKER_CONTAINER}:/tmp/npmplus-backup.tar.gz ${BACKUP_FILE} 2>&1 && \ pct exec ${CONTAINER_ID} -- docker exec ${DOCKER_CONTAINER} rm -f /tmp/npmplus-backup.tar.gz 2>&1 && \ test -f ${BACKUP_FILE} && echo \"SUCCESS\" || echo \"FAILED\"' 2>&1") if echo "$BACKUP_RESULT" | grep -q "SUCCESS"; then echo "Backup created successfully" else echo "Backup attempt completed (may have warnings)" fi BACKUP_RESULT=$(ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'test -f ${BACKUP_FILE} && echo \"exists\" || echo \"missing\"' 2>&1") if [ "$BACKUP_RESULT" = "exists" ]; then echo -e "${GREEN}✅ Backup created: ${BACKUP_FILE}${NC}" else echo -e "${YELLOW}⚠️ Backup may have failed, but continuing...${NC}" echo "Note: Data volumes are preserved in Docker, so risk is minimal" fi # Step 3: Pull new image echo -e "${BLUE}Step 3: Pulling new NPMplus image...${NC}" echo "This may take a few minutes (downloading ~500MB)..." echo "" # Try pulling from Proxmox host first (better network connectivity) echo "Attempting to pull from Proxmox host..." PULL_OUTPUT=$(ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 root@${NODE} \ 'timeout 180 docker pull ${NEW_IMAGE} 2>&1'" 2>&1) if echo "$PULL_OUTPUT" | grep -q "Downloaded\|Pulled\|up to date"; then echo -e "${GREEN}✅ Image pulled on Proxmox host${NC}" # Import to container's Docker echo "Importing image to container's Docker..." ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 root@${NODE} \ 'docker save ${NEW_IMAGE} | pct exec ${CONTAINER_ID} -- docker load 2>&1'" 2>&1 | tail -3 echo -e "${GREEN}✅ Image imported to container${NC}" else echo -e "${YELLOW}⚠️ Pull from host failed, trying from container...${NC}" # Try from container with longer timeout PULL_OUTPUT=$(ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=10 root@${NODE} \ 'timeout 180 pct exec ${CONTAINER_ID} -- docker pull ${NEW_IMAGE} 2>&1'" 2>&1 | tail -5) if echo "$PULL_OUTPUT" | grep -q "Downloaded\|Pulled\|up to date"; then echo -e "${GREEN}✅ Image pulled from container${NC}" else echo -e "${YELLOW}⚠️ Network timeout. Will try to pull during container creation.${NC}" echo "Note: Container creation will pull image if not available" fi fi # Step 4: Stop current container echo -e "${BLUE}Step 4: Stopping current container...${NC}" ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'pct exec ${CONTAINER_ID} -- docker stop ${DOCKER_CONTAINER} 2>&1'" 2>&1 sleep 2 # Step 5: Get volume mounts echo -e "${BLUE}Step 5: Getting volume configuration...${NC}" VOLUMES=$(ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'pct exec ${CONTAINER_ID} -- docker inspect ${DOCKER_CONTAINER} --format \"{{range .Mounts}}-v {{.Source}}:{{.Destination}} {{end}}\" 2>&1'" 2>&1) echo "Volumes: $VOLUMES" # Step 6: Remove old container echo -e "${BLUE}Step 6: Removing old container...${NC}" ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'pct exec ${CONTAINER_ID} -- docker rm ${DOCKER_CONTAINER} 2>&1'" 2>&1 # Step 7: Create new container with updated image echo -e "${BLUE}Step 7: Creating new container with updated image...${NC}" ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'pct exec ${CONTAINER_ID} -- docker run -d \ --name ${DOCKER_CONTAINER} \ --restart unless-stopped \ --network bridge \ -p 80:80 \ -p 443:443 \ -p 81:81 \ ${VOLUMES} \ ${NEW_IMAGE} 2>&1'" 2>&1 if [ $? -eq 0 ]; then echo -e "${GREEN}✅ New container created${NC}" else echo -e "${RED}❌ Failed to create container${NC}" echo "Restoring from backup..." # TODO: Add restore logic if needed exit 1 fi # Step 8: Wait for container to start echo -e "${BLUE}Step 8: Waiting for container to start...${NC}" sleep 5 CONTAINER_STATUS=$(ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'pct exec ${CONTAINER_ID} -- docker ps --filter name=${DOCKER_CONTAINER} --format \"{{.Status}}\" 2>&1'" 2>&1) if [ -n "$CONTAINER_STATUS" ]; then echo -e "${GREEN}✅ Container is running: ${CONTAINER_STATUS}${NC}" else echo -e "${RED}❌ Container is not running!${NC}" echo "Checking logs..." ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'pct exec ${CONTAINER_ID} -- docker logs ${DOCKER_CONTAINER} --tail 30 2>&1'" 2>&1 | tail -20 exit 1 fi # Step 9: Verify new version echo -e "${BLUE}Step 9: Verifying new version...${NC}" NEW_VERSION=$(ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'pct exec ${CONTAINER_ID} -- docker inspect ${DOCKER_CONTAINER} --format \"{{.Config.Image}}\" 2>&1'" 2>&1) echo "New version: $NEW_VERSION" if echo "$NEW_VERSION" | grep -q "2026-01-20-r2"; then echo -e "${GREEN}✅ Successfully updated to 2026-01-20-r2${NC}" else echo -e "${YELLOW}⚠️ Version check inconclusive${NC}" fi # Step 10: Test NPMplus accessibility echo -e "${BLUE}Step 10: Testing NPMplus accessibility...${NC}" sleep 10 # Give NPMplus time to fully start HTTP_167=$(curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 http://192.168.11.167:80 2>&1 || echo "000") if [ "$HTTP_167" = "200" ] || [ "$HTTP_167" = "301" ] || [ "$HTTP_167" = "302" ] || [ "$HTTP_167" = "308" ]; then echo -e "${GREEN}✅ NPMplus is accessible (HTTP ${HTTP_167})${NC}" else echo -e "${YELLOW}⚠️ NPMplus returned HTTP ${HTTP_167} (may need more time to start)${NC}" fi # Step 11: Test proxy functionality echo -e "${BLUE}Step 11: Testing proxy functionality...${NC}" PROXY_TEST=$(ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@192.168.11.10 \ "ssh -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5 root@${NODE} \ 'pct exec ${CONTAINER_ID} -- curl -s -H \"Host: explorer.d-bis.org\" -o /dev/null -w \"HTTP %{http_code}\" --connect-timeout 5 http://192.168.11.140:80 2>&1'" 2>&1) if echo "$PROXY_TEST" | grep -q "200"; then echo -e "${GREEN}✅ Proxy functionality working (${PROXY_TEST})${NC}" else echo -e "${YELLOW}⚠️ Proxy test: ${PROXY_TEST}${NC}" fi echo "" echo "==========================================" echo "Update Complete!" echo "==========================================" echo "" echo "Summary:" echo " - Old version: $CURRENT_VERSION" echo " - New version: $NEW_VERSION" echo " - Backup: ${BACKUP_FILE}" echo "" echo "Next steps:" echo " 1. Verify NPMplus dashboard: https://192.168.11.167:81" echo " 2. Test external access: curl -I https://explorer.d-bis.org" echo " 3. Configure Let's Encrypt certificate if needed" echo ""