diff --git a/infra/landing-backup/padelnomics-landing-backup.service b/infra/landing-backup/padelnomics-landing-backup.service index abc77fa..207fc37 100644 --- a/infra/landing-backup/padelnomics-landing-backup.service +++ b/infra/landing-backup/padelnomics-landing-backup.service @@ -5,6 +5,7 @@ Wants=network-online.target [Service] Type=oneshot +User=padelnomics_service EnvironmentFile=/opt/padelnomics/.env Environment=LANDING_DIR=/data/padelnomics/landing ExecStart=/usr/bin/rclone sync ${LANDING_DIR} :s3:${LITESTREAM_R2_BUCKET}/padelnomics/landing \ diff --git a/infra/setup_server.sh b/infra/setup_server.sh index bfda48f..a4b031f 100644 --- a/infra/setup_server.sh +++ b/infra/setup_server.sh @@ -1,36 +1,58 @@ #!/bin/bash -# One-time server setup: create app directory and GitLab deploy key. -# Run as root on a fresh server before deploying. +# One-time server setup. Run as root on a fresh server. +# Creates padelnomics_service user, installs system dependencies, +# and registers systemd services that run as that user. # # Usage: -# bash infra/setup_server.sh +# sudo bash infra/setup_server.sh set -euo pipefail APP_DIR="/opt/padelnomics" -KEY_PATH="$HOME/.ssh/padelnomics_deploy" +SERVICE_USER="padelnomics_service" +SERVICE_HOME="/home/${SERVICE_USER}" +KEY_PATH="${SERVICE_HOME}/.ssh/padelnomics_deploy" -# Create app directory +# Ensure running as root +if [ "$(id -u)" -ne 0 ]; then + echo "Error: must run as root (use sudo)" >&2 + exit 1 +fi + +# Create service user if not present +if ! id "$SERVICE_USER" &>/dev/null; then + useradd --system --create-home --shell /usr/sbin/nologin "$SERVICE_USER" + echo "Created user $SERVICE_USER" +else + echo "User $SERVICE_USER already exists, skipping" +fi + +# Add service user to docker group (needed for deploy.sh) +usermod -aG docker "$SERVICE_USER" +echo "Added $SERVICE_USER to docker group" + +# Create app directory owned by service user mkdir -p "$APP_DIR" +chown "$SERVICE_USER:$SERVICE_USER" "$APP_DIR" echo "Created $APP_DIR" -# Generate deploy key if not already present +# Generate deploy key as service user if not present if [ ! -f "$KEY_PATH" ]; then - mkdir -p "$HOME/.ssh" - ssh-keygen -t ed25519 -f "$KEY_PATH" -N "" -C "padelnomics-server" - chmod 700 "$HOME/.ssh" + sudo -u "$SERVICE_USER" mkdir -p "${SERVICE_HOME}/.ssh" + sudo -u "$SERVICE_USER" ssh-keygen -t ed25519 -f "$KEY_PATH" -N "" -C "padelnomics-server" + chmod 700 "${SERVICE_HOME}/.ssh" chmod 600 "$KEY_PATH" - chmod 644 "$KEY_PATH.pub" + chmod 644 "${KEY_PATH}.pub" # Configure SSH to use this key for gitlab.com - if ! grep -q "# padelnomics" "$HOME/.ssh/config" 2>/dev/null; then - cat >> "$HOME/.ssh/config" </dev/null; then + sudo -u "$SERVICE_USER" tee -a "${SERVICE_HOME}/.ssh/config" > /dev/null </dev/null; then echo "Installing rclone..." - curl -fsSL https://rclone.org/install.sh | sudo bash - echo "Installed rclone $(rclone version --check | head -1)" + curl -fsSL https://rclone.org/install.sh | bash + echo "Installed rclone $(rclone --version | head -1)" else echo "rclone already installed, skipping" fi -# Create landing data directory +# Create data directories owned by service user mkdir -p /data/padelnomics/landing +chown -R "$SERVICE_USER:$SERVICE_USER" /data/padelnomics echo "Created /data/padelnomics/landing" -# Install and enable landing backup timer +# Install and enable systemd services cp "$APP_DIR/infra/landing-backup/padelnomics-landing-backup.service" /etc/systemd/system/ cp "$APP_DIR/infra/landing-backup/padelnomics-landing-backup.timer" /etc/systemd/system/ +cp "$APP_DIR/infra/supervisor/padelnomics-supervisor.service" /etc/systemd/system/ systemctl daemon-reload systemctl enable --now padelnomics-landing-backup.timer echo "Enabled landing backup timer (every 30 min)" - -# Install and enable supervisor service -cp "$APP_DIR/infra/supervisor/padelnomics-supervisor.service" /etc/systemd/system/ -systemctl daemon-reload systemctl enable --now padelnomics-supervisor.service echo "Enabled supervisor service" @@ -71,10 +88,10 @@ echo "" echo "=== Next steps ===" echo "1. Add this deploy key to GitLab (Settings → Repository → Deploy Keys, read-only):" echo "" -cat "$KEY_PATH.pub" +cat "${KEY_PATH}.pub" echo "" -echo "2. Clone the repo:" -echo " git clone git@gitlab.com:YOUR_USER/padelnomics.git $APP_DIR" +echo "2. Clone the repo as $SERVICE_USER:" +echo " sudo -u $SERVICE_USER git clone git@gitlab.com:deemanone/padelnomics.git $APP_DIR" echo "" -echo "3. Deploy (first run installs sops+age and generates server keypair):" -echo " cd $APP_DIR && bash deploy.sh" +echo "3. Deploy (first run generates server age keypair — follow the printed instructions):" +echo " sudo -u $SERVICE_USER bash $APP_DIR/deploy.sh" diff --git a/infra/supervisor/padelnomics-supervisor.service b/infra/supervisor/padelnomics-supervisor.service index 169fff5..6616191 100644 --- a/infra/supervisor/padelnomics-supervisor.service +++ b/infra/supervisor/padelnomics-supervisor.service @@ -5,13 +5,13 @@ Wants=network-online.target [Service] Type=simple -User=root +User=padelnomics_service WorkingDirectory=/opt/padelnomics ExecStart=/bin/sh -c 'exec uv run python src/padelnomics/supervisor.py' Restart=always RestartSec=10 EnvironmentFile=/opt/padelnomics/.env -Environment=PATH=/root/.local/bin:/usr/local/bin:/usr/bin:/bin +Environment=PATH=/home/padelnomics_service/.local/bin:/usr/local/bin:/usr/bin:/bin Environment=LANDING_DIR=/data/padelnomics/landing Environment=DUCKDB_PATH=/data/padelnomics/lakehouse.duckdb Environment=SERVING_DUCKDB_PATH=/data/padelnomics/analytics.duckdb