Skip to main content

Lightsail Node.js host

This is the API box for re360muse-backend. RDS and R2 stay off this disk. Do not use the Bitnami Node.js blueprint — Ubuntu 24.04, same region as RDS (eu-west-1).

scripts/deploy.sh is the RealCredi-style installer. First run: apt upgrade, Node 20, Caddy, GitHub deploy key, clone, openssl rand -base64 32 for JWT / session / DB password, write .env, migrate deploy, systemd. Later releases: push main (self-hosted GitHub Actions runner on this box) or scripts/update.sh. Existing .env is never overwritten.

1. Create the instance​

  1. Lightsail → Create instance → Ubuntu 24.04 → 2–4 GB → re360muse-api.
  2. Attach a static IP.
  3. Networking: 22 from your IP; 80/443 open; not 4000 or 5432.
  4. Account → Advanced → VPC peering. RDS SG: 5432 from the Lightsail CIDR.

2. First boot (from your laptop)​

Copy the script, then run it as root on the box. It will print an SSH public key — add that as a read-only deploy key on MuseTec/re360muse-backend, press Enter, and it clones.

scp scripts/deploy.sh ubuntu@STATIC_IP:/tmp/re360muse-deploy.sh
ssh ubuntu@STATIC_IP
sudo bash /tmp/re360muse-deploy.sh \
--db-host re360muse.XXXX.eu-west-1.rds.amazonaws.com \
--s3-endpoint https://ACCOUNT_ID.r2.cloudflarestorage.com \
--aws-access-key "$AWS_ACCESS_KEY_ID" \
--aws-secret-key "$AWS_SECRET_ACCESS_KEY" \
--paystack-secret-key "$PAYSTACK_SECRET_KEY" \
--rds-admin-user re360muse_admin \
--rds-admin-password "$RDS_MASTER_PASSWORD" \
--seed

--db-password is optional. If omitted, the script generates one with openssl rand -base64 32 and builds:

DATABASE_URL=postgresql://re360muse_app:GENERATED@HOST:5432/re360muse?sslmode=require

--rds-admin-password applies create-app-user.sql over SSL. If you skip it, the SQL file is at /opt/re360muse/create-app-user.sql (mode 600) — run that as the master user from Lightsail, then re-run deploy if migrate failed.

Secrets are written to /opt/re360muse/api/.env mode 600 and not printed. Paystack and R2 keys are the flags you passed; JWT and session are generated.

Cloudflare Origin CA → /etc/caddy/certs/origin.pem and origin.key. DNS api A record → static IP, proxied, Full (strict), WebSockets on.

3. Later releases​

Push or merge to main on MuseTec/re360muse-backend. GitHub Actions compiles on a hosted runner, then a self-hosted runner on this box runs scripts/update.sh --branch main. develop does not deploy. SSH can stay limited to your IP — the runner dials out to GitHub.

One-time runner​

Mint a registration token (expires in one hour; do not commit it):

gh api -X POST repos/MuseTec/re360muse-backend/actions/runners/registration-token --jq .token

On the box. If install-github-runner.sh is not in the clone yet, scp it first (same pattern as first-boot deploy.sh):

scp scripts/install-github-runner.sh ubuntu@STATIC_IP:/tmp/install-github-runner.sh
ssh ubuntu@STATIC_IP
GH_RUNNER_TOKEN=... sudo bash /tmp/install-github-runner.sh
# later, after main has the script:
# GH_RUNNER_TOKEN=... sudo bash /opt/re360muse/api/scripts/install-github-runner.sh

Repo Settings → Actions → Runners should show re360muse-api idle. GitHub → Actions → Deploy Lightsail API → Run workflow is the manual trigger. Do not open port 22 to GitHub-hosted IPs.

Manual fallback​

sudo /opt/re360muse/api/scripts/update.sh
# same as:
sudo /opt/re360muse/api/scripts/deploy.sh

update.sh always npm ci (lockfile). prisma migrate deploy — not migrate dev / db push. --upgrade-os if you also want apt upgrade. --rotate-secrets regenerates JWT/session only.

If git pull has not landed a migration that production already needs (Prisma P2021), copy that folder from the laptop into /opt/re360muse/api/prisma/migrations/ then run npx prisma migrate deploy on the box (same pattern as a normal update). Commit the folder so the next update.sh keeps it.

curl -fsS http://127.0.0.1:4000/api/health
journalctl -u re360muse-api -f

Node binds 127.0.0.1:4000 in production (HOST). /api/docs is not served on this host. After first boot, tighten the box:

sudo bash /opt/re360muse/api/scripts/harden-host.sh

That enables UFW (22/80/443), fail2ban for sshd, and deletes leftover create-app-user.sql. It does not remove Lightsail’s ubuntu NOPASSWD:ALL — stripping that without an ubuntu password locks sudo.

Paystack webhook: https://api.re360muse.com/api/v1/payments/webhooks/paystack

4. Not on this box​

Postgres data directory, upload files as source of truth, .env in git, public 5432 or 4000.