#!/bin/bash # Server shared-infra setup: recover compose files, create service user, deploy. # Run as root on the server. # # Usage: # sudo bash setup.sh # # What it does: # 1. Recovers docker-compose.yml files from already-running containers (one-time) # 2. Creates infra_service system user (docker group, nologin) # 3. Creates /data/server-infra/{service}/ data directories # 4. Deploys all compose files from this repo to /opt/server-infra/ (idempotent) # # Padelnomics containers are recovered to /tmp/padelnomics-compose-recovered.yml only. # They belong in the padelnomics repo, not here. # # Services managed by this repo: # umami — analytics (recovered from running containers) # reverse-proxy — nginx proxy manager (recovered from running containers) # gitea — self-hosted git (new, compose file already in repo) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" SERVICE_USER="infra_service" DEPLOY_DIR="/opt/server-infra" [ "$(id -u)" = "0" ] || { echo "ERROR: Run as root: sudo bash setup.sh"; exit 1; } log() { echo "$(date '+%H:%M:%S') ==> $*"; } warn() { echo "$(date '+%H:%M:%S') ==> WARNING: $*" >&2; } # ── Preflight checks ─────────────────────────────────────────────────────────── command -v docker >/dev/null 2>&1 || { echo "ERROR: docker not found"; exit 1; } # Find uvx: prefer the installing user's local bin, fall back to PATH UVX="" for candidate in /home/Deeman/.local/bin/uvx /usr/local/bin/uvx "$(command -v uvx 2>/dev/null || true)"; do if [ -n "${candidate}" ] && [ -x "${candidate}" ]; then UVX="${candidate}" break fi done [ -n "${UVX}" ] || { echo "ERROR: uvx not found — install uv first: curl -LsSf https://astral.sh/uv/install.sh | sh"; exit 1; } log "Using uvx at ${UVX}" # ── Helper: recover one compose project ─────────────────────────────────────── # Usage: recover_project [ ...] recover_project() { local outfile="$1" shift local containers=("$@") # Check at least one container from the list is running local running=0 for c in "${containers[@]}"; do if docker inspect "${c}" >/dev/null 2>&1; then running=1 break fi done if [ "${running}" = "0" ]; then warn "None of [${containers[*]}] are running — skipping recovery." return 0 fi log "Recovering compose file for: ${containers[*]}" "${UVX}" docker-autocompose "${containers[@]}" > "${outfile}" log "Saved to ${outfile}" } # ── Recover compose files ────────────────────────────────────────────────────── log "Recovering compose files from running containers..." mkdir -p /tmp/server-infra-recovery recover_project \ /tmp/server-infra-recovery/umami.yml \ umami-umami-1 umami-db-1 recover_project \ /tmp/server-infra-recovery/reverse-proxy.yml \ reverse_proxy-app-1 recover_project \ /tmp/padelnomics-compose-recovered.yml \ padelnomics-blue-worker-1 \ padelnomics-blue-scheduler-1 \ padelnomics-blue-app-1 \ padelnomics-litestream-1 \ padelnomics-router-1 # ── Copy recovered files into this repo ─────────────────────────────────────── log "Copying recovered files into repo (${SCRIPT_DIR})..." for pair in \ "/tmp/server-infra-recovery/umami.yml:${SCRIPT_DIR}/umami/docker-compose.yml" \ "/tmp/server-infra-recovery/reverse-proxy.yml:${SCRIPT_DIR}/reverse-proxy/docker-compose.yml"; do src="${pair%%:*}" dst="${pair##*:}" if [ -f "${src}" ]; then mkdir -p "$(dirname "${dst}")" cp "${src}" "${dst}" log " ${dst}" fi done # ── Service account ──────────────────────────────────────────────────────────── log "Creating service user ${SERVICE_USER}..." if ! id "${SERVICE_USER}" >/dev/null 2>&1; then useradd --system --create-home --shell /usr/sbin/nologin "${SERVICE_USER}" log " User created." else log " User already exists — skipping." fi usermod -aG docker "${SERVICE_USER}" # ── Data directories ─────────────────────────────────────────────────────────── log "Creating data directories..." mkdir -p /data/server-infra/gitea chown -R "${SERVICE_USER}:${SERVICE_USER}" /data/server-infra # ── Deploy: copy all compose files from repo to deploy dir (idempotent) ──────── # # Recovered files (umami, reverse-proxy) are already in ${SCRIPT_DIR} after # the recovery section above. Gitea compose is committed directly in the repo. # This section deploys all of them to DEPLOY_DIR. log "Deploying compose files to ${DEPLOY_DIR}..." for service in umami reverse-proxy gitea; do src="${SCRIPT_DIR}/${service}/docker-compose.yml" dst="${DEPLOY_DIR}/${service}/docker-compose.yml" if [ -f "${src}" ]; then mkdir -p "${DEPLOY_DIR}/${service}" cp "${src}" "${dst}" log " ${service} → ${dst}" else warn " ${service}/docker-compose.yml not in repo — skipping deploy" fi done chown -R "${SERVICE_USER}:${SERVICE_USER}" "${DEPLOY_DIR}" # ── Summary ──────────────────────────────────────────────────────────────────── echo "" echo "==================================================================" echo "" echo " Setup complete." echo "" echo " Deployed services:" [ -f "${DEPLOY_DIR}/umami/docker-compose.yml" ] && echo " umami → ${DEPLOY_DIR}/umami/docker-compose.yml" [ -f "${DEPLOY_DIR}/reverse-proxy/docker-compose.yml" ] && echo " reverse-proxy → ${DEPLOY_DIR}/reverse-proxy/docker-compose.yml" [ -f "${DEPLOY_DIR}/gitea/docker-compose.yml" ] && echo " gitea → ${DEPLOY_DIR}/gitea/docker-compose.yml" echo "" if [ -f "/tmp/padelnomics-compose-recovered.yml" ]; then echo " Padelnomics (NOT in repo — save to padelnomics repo):" echo " /tmp/padelnomics-compose-recovered.yml" echo "" fi echo " Service user: ${SERVICE_USER} (docker group)" echo " Deploy dir: ${DEPLOY_DIR}/" echo " Data dir: /data/server-infra/" echo "" echo "==================================================================" echo "" echo " Next steps:" echo "" echo " 1. Commit recovered compose files to git:" echo " cd ${SCRIPT_DIR}" echo " git add umami/docker-compose.yml reverse-proxy/docker-compose.yml" echo " git commit -m 'chore: recover compose files from running containers'" echo "" echo " 2. Start Gitea:" echo " sudo -u ${SERVICE_USER} docker compose -f ${DEPLOY_DIR}/gitea/docker-compose.yml up -d" echo " # Then open http://:3000 to complete the web installer" echo " # Set ROOT_URL to your public domain (e.g. https://git.yourdomain.com)" echo "" echo " 3. Add Gitea proxy host in nginx proxy manager:" echo " Forward to :3000" echo "" echo " 4. To restart any service if containers go down:" echo " sudo -u ${SERVICE_USER} docker compose -f ${DEPLOY_DIR}//docker-compose.yml up -d" echo "" echo " 5. Move padelnomics compose to the padelnomics repo:" echo " /tmp/padelnomics-compose-recovered.yml" echo "" echo "==================================================================" echo ""