Files
server-infra/setup.sh
2026-02-27 16:02:44 +01:00

178 lines
7.1 KiB
Bash

#!/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)
#
# 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 /root/.local/bin/uvx /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 <outfile> <container_name> [<container_name> ...]
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
# ── 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 ""
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://<server-ip>: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 <server-ip>:3000"
echo ""
echo " 4. To restart any service if containers go down:"
echo " sudo -u ${SERVICE_USER} docker compose -f ${DEPLOY_DIR}/<service>/docker-compose.yml up -d"
echo ""
echo "=================================================================="
echo ""