diff --git a/README.md b/README.md index cf24a9b..0089216 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,30 @@ Docker Compose does not support time-based restarts. To force a daily restart: - launchd (macOS): create `~/Library/LaunchAgents/com.ploughshares.dailyrestart.plist` with a StartCalendarInterval and run a script that executes `docker-compose -f docker-compose.yml down && docker-compose -f docker-compose.yml up -d` in the repo directory. +## Server Install (Ubuntu) + +Non-interactive systemd install that deploys to /opt/ploughshares: + +```bash +sudo ./install.sh +``` + +What it does: +- Installs Docker + compose plugin if missing +- Syncs repo to /opt/ploughshares +- Ensures /opt/ploughshares/.env exists (copies from .env.example if absent) +- Installs systemd unit ploughshares-compose.service and daily timer ploughshares-daily-restart.timer +- Enables and starts both + +Change restart time: + +```bash +sudo systemctl edit ploughshares-daily-restart.timer +# Add in [Timer]: OnCalendar=*-*-* 04:00:00 then +sudo systemctl daemon-reload +sudo systemctl restart ploughshares-daily-restart.timer +``` + ### Crawler - Google Alerts To run the Google Alerts crawler locally (requires GOOGLE_API_KEY): diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..370cb5f --- /dev/null +++ b/install.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Non-interactive installer for Ubuntu hosts. +# - Installs docker and docker compose plugin if missing +# - Installs repo under /opt/ploughshares (uses current repo copy) +# - Ensures .env exists (creates from .env.example if missing) +# - Installs systemd unit and timer for stack management and daily restart +# - Enables and starts services + +set -euo pipefail + +if [ "${EUID:-$(id -u)}" -ne 0 ]; then + echo "This install.sh must be run as root (sudo)." + exit 1 +fi + +SRC_DIR="$(cd "$(dirname "$0")" && pwd)" +DEST_DIR="/opt/ploughshares" +SYSTEMD_DIR="/etc/systemd/system" + +echo "=== Installing prerequisites (docker, compose) ===" +if ! command -v docker >/dev/null 2>&1; then + apt-get update + apt-get install -y ca-certificates curl gnupg + install -m 0755 -d /etc/apt/keyrings + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg + chmod a+r /etc/apt/keyrings/docker.gpg + echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ + $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ + tee /etc/apt/sources.list.d/docker.list > /dev/null + apt-get update + apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + systemctl enable --now docker +fi + +echo "=== Deploying repo to $DEST_DIR ===" +mkdir -p "$DEST_DIR" +rsync -a --delete "$SRC_DIR/" "$DEST_DIR/" + +echo "=== Ensuring .env exists ===" +if [ ! -f "$DEST_DIR/.env" ]; then + if [ -f "$DEST_DIR/.env.example" ]; then + cp "$DEST_DIR/.env.example" "$DEST_DIR/.env" + echo "Created $DEST_DIR/.env from example. Update secrets as needed." + else + touch "$DEST_DIR/.env" + echo "Created empty $DEST_DIR/.env." + fi +fi + +echo "=== Installing systemd unit and timer ===" +install -m 0644 "$DEST_DIR/systemd/ploughshares-compose.service" "$SYSTEMD_DIR/ploughshares-compose.service" +install -m 0644 "$DEST_DIR/systemd/ploughshares-daily-restart.service" "$SYSTEMD_DIR/ploughshares-daily-restart.service" +install -m 0644 "$DEST_DIR/systemd/ploughshares-daily-restart.timer" "$SYSTEMD_DIR/ploughshares-daily-restart.timer" + +systemctl daemon-reload +systemctl enable ploughshares-compose.service +systemctl start ploughshares-compose.service + +systemctl enable ploughshares-daily-restart.timer +systemctl start ploughshares-daily-restart.timer + +echo "=== Verifying services ===" +systemctl --no-pager status ploughshares-compose.service | sed -n '1,20p' +systemctl --no-pager status ploughshares-daily-restart.timer | sed -n '1,20p' + +echo "Install complete. Repo at $DEST_DIR. Edit $DEST_DIR/.env to set secrets." + + diff --git a/systemd/ploughshares-compose.service b/systemd/ploughshares-compose.service new file mode 100644 index 0000000..9e6e02b --- /dev/null +++ b/systemd/ploughshares-compose.service @@ -0,0 +1,19 @@ +[Unit] +Description=Ploughshares Docker Compose Stack +Requires=docker.service +After=docker.service network-online.target +Wants=network-online.target + +[Service] +Type=oneshot +RemainAfterExit=yes +WorkingDirectory=/opt/ploughshares +Environment=COMPOSE_FILE=docker-compose.yml +ExecStart=/usr/bin/docker compose -f ${COMPOSE_FILE} up -d +ExecStop=/usr/bin/docker compose -f ${COMPOSE_FILE} down +TimeoutStartSec=0 + +[Install] +WantedBy=multi-user.target + + diff --git a/systemd/ploughshares-daily-restart.service b/systemd/ploughshares-daily-restart.service new file mode 100644 index 0000000..934d279 --- /dev/null +++ b/systemd/ploughshares-daily-restart.service @@ -0,0 +1,12 @@ +[Unit] +Description=Daily restart of Ploughshares Docker Compose Stack +Requires=ploughshares-compose.service +After=ploughshares-compose.service + +[Service] +Type=oneshot +WorkingDirectory=/opt/ploughshares +Environment=COMPOSE_FILE=docker-compose.yml +ExecStart=/bin/bash -lc '/opt/ploughshares/restart_containers.sh >> /var/log/ploughshares-restart.log 2>&1' + + diff --git a/systemd/ploughshares-daily-restart.timer b/systemd/ploughshares-daily-restart.timer new file mode 100644 index 0000000..0bdc1e5 --- /dev/null +++ b/systemd/ploughshares-daily-restart.timer @@ -0,0 +1,13 @@ +[Unit] +Description=Daily restart timer for Ploughshares Docker Compose Stack + +[Timer] +# Default daily at 03:15 local time. Change with: systemctl edit ploughshares-daily-restart.timer +OnCalendar=*-*-* 03:15:00 +Persistent=true +Unit=ploughshares-daily-restart.service + +[Install] +WantedBy=timers.target + +