Files
michaelandClaude Fable 5.1 3aa7fb7b83
Deploy Waters Boards to AWS / deploy (push) Canceled after 0s
Host on AWS Lightsail as boards.watersmedia.org
Replace the planned Synology deploy with one Lightsail box: Caddy in front of the
app for automatic HTTPS (deploy/aws/docker-compose.yml, Caddyfile), a deploy
script that rsyncs server/ and rebuilds in place without touching data/ or .env,
and a Gitea workflow that runs the same script from the nas runner over SSH.
README documents the box, DNS, .env, deploys and restore. Allow both church
Google Workspace domains to sign in. Drop the NAS-only compose file.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-14 14:20:25 -04:00

79 lines
2.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# Deploy Waters Boards to the AWS Lightsail box.
#
# Usage, from the repo root:
# deploy/aws/deploy.sh
#
# Target host comes from BOARDS_HOST (default: the "boards-aws" SSH alias).
# Never writes or deletes data/ or .env on the box. Uses ssh BatchMode, so it never prompts.
set -euo pipefail
HOST="${BOARDS_HOST:-boards-aws}"
REMOTE_DIR=/opt/waters-boards
SSH_OPTS=(-o BatchMode=yes)
cd "$(dirname "$0")/../.."
step() { printf '\n==> %s\n' "$*"; }
remote() { ssh "${SSH_OPTS[@]}" "$HOST" "$@"; }
step "1/7 Syntax-check the server code"
node --check server/server.js
for f in server/src/*.js; do
node --check "$f"
done
echo "syntax OK"
step "2/7 Check $HOST:$REMOTE_DIR/.env exists"
if ! remote "test -f $REMOTE_DIR/.env"; then
echo "ABORT: $REMOTE_DIR/.env is missing on $HOST (or $HOST is unreachable)." >&2
echo "Create it once by hand from server/.env.example. This script never creates or overwrites it." >&2
echo "See README.md > Deploy to AWS (Lightsail)." >&2
exit 1
fi
echo ".env found"
step "3/7 Copy server/ to $HOST:$REMOTE_DIR/server/ (no data/, .env or node_modules/, no --delete)"
rsync -a -e "ssh ${SSH_OPTS[*]}" \
--exclude 'data/' --exclude '.env' --exclude 'node_modules/' \
server/ "$HOST:$REMOTE_DIR/server/"
step "4/7 Copy docker-compose.yml and Caddyfile to $HOST:$REMOTE_DIR/"
# --inplace: the Caddyfile is bind-mounted as a single file. A normal rsync replaces it with a
# new file, and the running container would keep reading the old one.
rsync -a --inplace -e "ssh ${SSH_OPTS[*]}" \
deploy/aws/docker-compose.yml deploy/aws/Caddyfile "$HOST:$REMOTE_DIR/"
step "5/7 Rebuild and restart in place (no 'down' first: a failed build leaves the old container serving)"
remote "cd $REMOTE_DIR && docker compose up -d --build --remove-orphans"
# Caddy doesn't watch its config. Reload so a changed Caddyfile takes effect without a restart.
remote "cd $REMOTE_DIR && docker compose exec -T caddy caddy reload --config /etc/caddy/Caddyfile" \
|| echo "WARNING: caddy reload failed; check: docker logs waters-caddy" >&2
step "6/7 Wait for the waters-boards container to report healthy (up to 20 x 3 s)"
healthy=0
for i in $(seq 1 20); do
status=$(remote "docker inspect --format '{{.State.Health.Status}}' waters-boards" 2>/dev/null || echo starting)
echo " try $i/20: $status"
if [ "$status" = "healthy" ]; then
healthy=1
break
fi
sleep 3
done
if [ "$healthy" -ne 1 ]; then
echo "ABORT: waters-boards did not report healthy in time." >&2
remote "docker ps" || true
remote "docker logs --tail 30 waters-boards" || true
exit 1
fi
echo "container healthy"
step "7/7 Check the public URL (informational only; DNS or the certificate may not be ready yet)"
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 15 https://boards.watersmedia.org/healthz || true)
echo "https://boards.watersmedia.org/healthz -> HTTP ${code:-000}"
echo
echo "Deploy finished."