fix(install): lite deploy for small VPS disks

Run Postgres/Redis in Docker and build Next.js on host to avoid
image build exhausting 10GB root volumes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Андрей Бобырев
2026-05-24 22:22:55 +03:00
parent ab5c0084b2
commit 9c7bd392e9

View File

@@ -1,10 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Domain Scanner — one-command VPS install (Docker Compose + nginx) # Domain Scanner — VPS install (Postgres/Redis in Docker, Next.js on host)
# Full `docker compose up` (web image) needs ~4GB free disk; this script uses the lite path.
set -euo pipefail set -euo pipefail
REPO_URL="${DOMAIN_SCANNER_REPO_URL:-https://github.com/andrey271192/Domain_web.git}" REPO_URL="${DOMAIN_SCANNER_REPO_URL:-https://github.com/andrey271192/Domain_web.git}"
BRANCH="${DOMAIN_SCANNER_BRANCH:-main}" BRANCH="${DOMAIN_SCANNER_BRANCH:-main}"
INSTALL_DIR="${DOMAIN_SCANNER_INSTALL_DIR:-/opt/domain_web}" INSTALL_DIR="${DOMAIN_SCANNER_INSTALL_DIR:-/opt/domain-scanner}"
SERVICE_NAME="domain-scanner" SERVICE_NAME="domain-scanner"
NGINX_SITE="domain-scanner" NGINX_SITE="domain-scanner"
APP_PORT="${DOMAIN_SCANNER_PORT:-3000}" APP_PORT="${DOMAIN_SCANNER_PORT:-3000}"
@@ -27,16 +28,26 @@ install_packages() {
apt-get install -y -qq curl ca-certificates git nginx openssl >/dev/null apt-get install -y -qq curl ca-certificates git nginx openssl >/dev/null
} }
install_node() {
if command -v node >/dev/null 2>&1 && [[ "$(node -p 'process.versions.node.split(".")[0]')" -ge 20 ]]; then
log "Node $(node -v) already installed"
return
fi
log "Installing Node.js 22..."
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -y -qq nodejs >/dev/null
}
install_docker() { install_docker() {
if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
log "Docker already installed" log "Docker already installed"
return else
fi
log "Installing Docker..." log "Installing Docker..."
curl -fsSL https://get.docker.com | sh curl -fsSL https://get.docker.com | sh
systemctl enable docker.socket docker.service fi
systemctl start docker.socket systemctl enable docker.socket docker.service 2>/dev/null || true
systemctl start docker.service systemctl start docker.socket 2>/dev/null || true
systemctl start docker.service 2>/dev/null || true
} }
stop_legacy() { stop_legacy() {
@@ -46,6 +57,7 @@ stop_legacy() {
rm -f "/etc/systemd/system/${LEGACY_SERVICE}.service" rm -f "/etc/systemd/system/${LEGACY_SERVICE}.service"
rm -f "/etc/nginx/sites-enabled/${LEGACY_NGINX}" rm -f "/etc/nginx/sites-enabled/${LEGACY_NGINX}"
rm -f "/etc/nginx/sites-available/${LEGACY_NGINX}" rm -f "/etc/nginx/sites-available/${LEGACY_NGINX}"
rm -rf /opt/domain_web 2>/dev/null || true
systemctl daemon-reload 2>/dev/null || true systemctl daemon-reload 2>/dev/null || true
} }
@@ -73,35 +85,64 @@ setup_env() {
sed -i "s|^NEXTAUTH_SECRET=.*|NEXTAUTH_SECRET=${secret}|" .env sed -i "s|^NEXTAUTH_SECRET=.*|NEXTAUTH_SECRET=${secret}|" .env
sed -i "s|^NEXTAUTH_URL=.*|NEXTAUTH_URL=http://${ip}|" .env sed -i "s|^NEXTAUTH_URL=.*|NEXTAUTH_URL=http://${ip}|" .env
sed -i "s|^NEXT_PUBLIC_APP_URL=.*|NEXT_PUBLIC_APP_URL=http://${ip}|" .env sed -i "s|^NEXT_PUBLIC_APP_URL=.*|NEXT_PUBLIC_APP_URL=http://${ip}|" .env
export PORT="${APP_PORT}" sed -i "s|^DATABASE_URL=.*|DATABASE_URL=postgresql://scanner:scanner@127.0.0.1:5432/domain_scanner?schema=public|" .env
sed -i "s|^REDIS_URL=.*|REDIS_URL=redis://127.0.0.1:6379|" .env
} }
deploy_compose() { deploy_data_services() {
log "Starting Postgres and Redis (Docker)..."
cd "${INSTALL_DIR}" cd "${INSTALL_DIR}"
export PORT="${APP_PORT}" docker compose up -d postgres redis
AVAIL_KB=$(df -k "${INSTALL_DIR}" | awk 'NR==2 {print $4}') for i in $(seq 1 30); do
if [[ "${AVAIL_KB:-0}" -lt 2500000 ]]; then if docker compose exec -T postgres pg_isready -U scanner -d domain_scanner >/dev/null 2>&1; then
log "Low disk — Node-native deploy..." break
bash "${INSTALL_DIR}/scripts/install-node.sh"
return
fi
log "Building Docker stack..."
if docker compose version >/dev/null 2>&1; then
if ! docker compose build 2>&1; then
log "Docker build failed — Node-native deploy..."
bash "${INSTALL_DIR}/scripts/install-node.sh"
return
fi
docker compose up -d
docker compose exec -T web npx prisma db push
else
if ! docker-compose build 2>&1; then
bash "${INSTALL_DIR}/scripts/install-node.sh"
return
fi
docker-compose up -d
docker-compose exec -T web npx prisma db push
fi fi
sleep 2
done
}
build_app() {
log "Building Next.js app on host..."
cd "${INSTALL_DIR}"
export NODE_ENV=production
npm ci
npx prisma generate
npx prisma db push
npm run build
rm -rf node_modules
npm ci --omit=dev
npx prisma generate
cp -r .next/static .next/standalone/.next/static
cp -r public .next/standalone/public
journalctl --vacuum-size=80M 2>/dev/null || true
apt-get clean 2>/dev/null || true
}
install_systemd() {
log "Installing systemd unit (${SERVICE_NAME})..."
cat >"/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=Domain Scanner (Next.js)
After=network.target docker.service
Wants=docker.service
[Service]
Type=simple
WorkingDirectory=${INSTALL_DIR}/.next/standalone
EnvironmentFile=${INSTALL_DIR}/.env
Environment=HOSTNAME=0.0.0.0
Environment=PORT=${APP_PORT}
Environment=NODE_ENV=production
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable "${SERVICE_NAME}.service"
systemctl restart "${SERVICE_NAME}.service"
} }
install_nginx() { install_nginx() {
@@ -138,10 +179,14 @@ EOF
verify() { verify() {
log "Verifying deployment..." log "Verifying deployment..."
sleep 5 sleep 4
if ! systemctl is-active --quiet "${SERVICE_NAME}.service"; then
systemctl status "${SERVICE_NAME}.service" --no-pager || true
journalctl -u "${SERVICE_NAME}.service" -n 30 --no-pager || true
exit 1
fi
if ! curl -fsS -o /dev/null "http://127.0.0.1:${APP_PORT}/api/health"; then if ! curl -fsS -o /dev/null "http://127.0.0.1:${APP_PORT}/api/health"; then
echo "App health check failed on port ${APP_PORT}" >&2 echo "Health check failed on port ${APP_PORT}" >&2
cd "${INSTALL_DIR}" && docker compose logs --tail=40 web 2>/dev/null || true
exit 1 exit 1
fi fi
local title local title
@@ -150,22 +195,21 @@ verify() {
echo "ERROR: GeoExport mirror still served — aborting" >&2 echo "ERROR: GeoExport mirror still served — aborting" >&2
exit 1 exit 1
fi fi
if ! curl -fsS -o /dev/null "http://127.0.0.1/"; then
echo "nginx not serving on :80" >&2
exit 1
fi
local ip local ip
ip="$(hostname -I | awk '{print $1}')" ip="$(hostname -I | awk '{print $1}')"
log "Done. Domain Scanner: http://${ip}/" log "Done. Domain Scanner: http://${ip}/"
log "Title check: ${title:-ok}" log "Title: ${title:-Domain Scanner}"
} }
need_root need_root
install_packages install_packages
install_node
install_docker install_docker
stop_legacy stop_legacy
clone_or_update clone_or_update
setup_env setup_env
deploy_compose deploy_data_services
build_app
install_systemd
install_nginx install_nginx
verify verify