| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118 |
- #!/usr/bin/env bash
- # Move a Docker Compose project and its named volumes to another host.
- # Source host: this machine
- # Destination host: 192.168.8.80
- # Project directory on both hosts: /home/tas_councils
- set -euo pipefail
- # ------------------------- CONFIG -------------------------
- DEST_HOST="192.168.8.80"
- DEST_USER="${USER}" # change if the username differs on the new server
- PROJECT_DIR="/home/tas_councils"
- COMPOSE_FILE="${PROJECT_DIR}/docker-compose.yml"
- SSH_OPTS="-o StrictHostKeyChecking=accept-new -o ConnectTimeout=5"
- # ----------------------------------------------------------
- say() { printf "\n%s\n" "$*"; }
- need() {
- command -v "$1" >/dev/null 2>&1 || { echo "Missing dependency: $1"; exit 1; }
- }
- # Prefer 'docker compose', fall back to 'docker-compose'
- dc() {
- if docker compose version >/dev/null 2>&1; then
- docker compose "$@"
- else
- docker-compose "$@"
- fi
- }
- # Remote 'docker compose' wrapper
- remote_dc() {
- local cmd="$*"
- ssh ${SSH_OPTS} "${DEST_USER}@${DEST_HOST}" "bash -lc '
- if docker compose version >/dev/null 2>&1; then dcc=\"docker compose\"; else dcc=\"docker-compose\"; fi
- cd \"${PROJECT_DIR}\" && \$dcc ${cmd}
- '"
- }
- # ----------------------- PRECHECKS ------------------------
- need docker
- need rsync
- need ssh
- [[ -d "${PROJECT_DIR}" ]] || { echo "Project dir not found: ${PROJECT_DIR}"; exit 1; }
- [[ -f "${COMPOSE_FILE}" ]] || { echo "Compose file not found: ${COMPOSE_FILE}"; exit 1; }
- say "Checking SSH access to ${DEST_USER}@${DEST_HOST}..."
- ssh ${SSH_OPTS} "${DEST_USER}@${DEST_HOST}" 'echo "OK from $(hostname)"' >/dev/null
- say "Checking Docker on destination..."
- ssh ${SSH_OPTS} "${DEST_USER}@${DEST_HOST}" 'docker version >/dev/null 2>&1' \
- || { echo "Docker not available on destination"; exit 1; }
- say "Creating project directory on destination..."
- ssh ${SSH_OPTS} "${DEST_USER}@${DEST_HOST}" "mkdir -p '${PROJECT_DIR}'"
- # Determine project name
- PROJECT_NAME="${PROJECT_NAME:-$(basename "${PROJECT_DIR}")}"
- if [[ -f "${PROJECT_DIR}/.env" ]]; then
- # If .env sets COMPOSE_PROJECT_NAME, use it
- ENV_NAME="$(grep -E '^\s*COMPOSE_PROJECT_NAME\s*=' "${PROJECT_DIR}/.env" | tail -n1 | cut -d= -f2- | tr -d '[:space:]' || true)"
- if [[ -n "${ENV_NAME}" ]]; then
- PROJECT_NAME="${ENV_NAME}"
- fi
- fi
- say "Project name detected: ${PROJECT_NAME}"
- # --------------------- STOP SOURCE STACK ------------------
- say "Stopping stack on source host..."
- dc -f "${COMPOSE_FILE}" -p "${PROJECT_NAME}" down
- # -------------------- SYNC PROJECT DIR --------------------
- say "Syncing project directory to destination..."
- rsync -a --delete -e "ssh ${SSH_OPTS}" "${PROJECT_DIR}/" "${DEST_USER}@${DEST_HOST}:${PROJECT_DIR}/"
- # -------------------- COPY NAMED VOLUMES ------------------
- say "Finding named volumes for the project..."
- mapfile -t VOLUMES < <(docker volume ls --format '{{.Name}}' | grep -E "^${PROJECT_NAME}_" || true)
- if [[ ${#VOLUMES[@]} -eq 0 ]]; then
- say "No named volumes found with prefix ${PROJECT_NAME}_"
- else
- say "Will copy ${#VOLUMES[@]} volume(s):"
- printf ' - %s\n' "${VOLUMES[@]}"
- for VOL in "${VOLUMES[@]}"; do
- say "Copying volume: ${VOL}"
- # Create volume on destination if it does not exist
- ssh ${SSH_OPTS} "${DEST_USER}@${DEST_HOST}" "docker volume inspect '${VOL}' >/dev/null 2>&1 || docker volume create '${VOL}' >/dev/null"
- # Stream tar from source volume into destination volume
- docker run --rm -v "${VOL}:/src:ro" busybox sh -c "cd /src && tar -cpf - ." \
- | ssh ${SSH_OPTS} "${DEST_USER}@${DEST_HOST}" "docker run --rm -i -v '${VOL}:/dest' busybox sh -c 'cd /dest && tar -xpf -'"
- done
- fi
- # ------------------ START DESTINATION STACK ----------------
- say "Starting stack on destination host..."
- # Pull images first. If the destination has no Internet, comment out the pull line.
- ssh ${SSH_OPTS} "${DEST_USER}@${DEST_HOST}" "bash -lc '
- cd \"${PROJECT_DIR}\"
- if docker compose version >/dev/null 2>&1; then dcc=\"docker compose\"; else dcc=\"docker-compose\"; fi
- \$dcc pull || true
- \$dcc up -d
- '"
- say "Destination stack status:"
- ssh ${SSH_OPTS} "${DEST_USER}@${DEST_HOST}" "bash -lc '
- cd \"${PROJECT_DIR}\"
- if docker compose version >/dev/null 2>&1; then dcc=\"docker compose\"; else dcc=\"docker-compose\"; fi
- \$dcc ps
- '"
- say "Done. Your Compose project and its named volumes have been moved to ${DEST_HOST}."
- say "If you used bind mounts outside ${PROJECT_DIR}, review those paths and copy them as needed."
|