# OrthoGlobe deploy — native Ubuntu 24.04 VPS

Target: **Contabo Cloud VPS 30 NVMe** at `158.220.127.51`.

Stack (all native, no Docker):

- **Node.js 22 LTS** running the Next standalone bundle via systemd
- **PostgreSQL 16** (PGDG) on `127.0.0.1:5432`
- **Redis 7** on `127.0.0.1:6379`
- **Caddy 2** reverse proxy with automatic HTTPS (Let's Encrypt)

Uploads live on disk at `/home/deploy/orthoglobe-uploads` and are symlinked
into the app's `public/uploads` so they survive every redeploy.

---

## Fresh deploy from scratch

Files involved:

- `deploy/scripts/remote-provision.sh` — run once per new box (wipes + installs)
- `deploy/scripts/remote-app-start.sh` — run on every deploy (build + seed + restart)

### 1. Upload the repo

From your laptop, package + SCP:

```bash
# Git Bash on Windows — pscp is bundled with PuTTY
cd "/d/Work/Special Projects/New OrthoGlobe"

# Make a clean tarball without node_modules, .next, etc.
tar --exclude='site/node_modules' --exclude='site/.next' \
    --exclude='site/.git' --exclude='site/.env*' \
    --exclude='site/legacy' --exclude='site/wp-migration-report-*.json' \
    --exclude='site/.playwright-mcp' --exclude='site/.pnpm-store' \
    -czf orthoglobe.tar.gz site

pscp -pw AtefAbdo99 orthoglobe.tar.gz [email protected]:/root/
```

### 2. Run the provision script

```bash
plink -pw AtefAbdo99 [email protected] \
  "tar xzf /root/orthoglobe.tar.gz -C /root && \
   bash /root/site/deploy/scripts/remote-provision.sh"
```

Installs Node 22, Postgres 16, Redis 7, Caddy 2. Creates the `deploy` user
and the `orthoglobe` database. Writes credentials to
`/etc/orthoglobe/db.env`.

### 3. Move the repo to the deploy user and start the app

```bash
plink -pw AtefAbdo99 [email protected] "
  rm -rf /home/deploy/orthoglobe
  mv /root/site /home/deploy/orthoglobe
  chown -R deploy:deploy /home/deploy/orthoglobe
  sudo -u deploy bash /home/deploy/orthoglobe/deploy/scripts/remote-app-start.sh
"
```

First run builds the app (~3–5 min), seeds email templates + admin roster
+ sample store/research data, writes the systemd unit, writes the
Caddyfile, and reloads Caddy.

### 4. Point your domain

Default domain while DNS is unset: `158-220-127-51.nip.io` (auto-resolves
to the IP — handy for Let's Encrypt on day 0).

When ready, add at your registrar:

| Host | Type | Value |
|---|---|---|
| `@`   | A | `158.220.127.51` |
| `www` | A | `158.220.127.51` |

Then swap the domain:

```bash
plink -pw AtefAbdo99 [email protected] "
  echo 'orthoglobe.org' | sudo tee /etc/orthoglobe/domain
  sudo sed -i 's#NEXTAUTH_URL=.*#NEXTAUTH_URL=https://orthoglobe.org#' /etc/orthoglobe/app.env
  sudo -u deploy bash /home/deploy/orthoglobe/deploy/scripts/remote-app-start.sh
"
```

---

## Subsequent deploys

Just re-upload + rerun the start script:

```bash
# From laptop
tar --exclude='site/node_modules' --exclude='site/.next' \
    --exclude='site/.git' --exclude='site/.env*' \
    -czf orthoglobe.tar.gz site
pscp -pw AtefAbdo99 orthoglobe.tar.gz [email protected]:/tmp/
plink -pw AtefAbdo99 [email protected] "
  sudo systemctl stop orthoglobe
  sudo rm -rf /home/deploy/orthoglobe
  sudo tar xzf /tmp/orthoglobe.tar.gz -C /home/deploy
  sudo mv /home/deploy/site /home/deploy/orthoglobe
  sudo chown -R deploy:deploy /home/deploy/orthoglobe
  sudo -u deploy bash /home/deploy/orthoglobe/deploy/scripts/remote-app-start.sh
"
```

---

## Day-2 operations

| Task | Command |
|---|---|
| App logs | `journalctl -u orthoglobe -f` |
| Caddy logs | `journalctl -u caddy -f` |
| psql prompt | `sudo -u postgres psql orthoglobe` |
| Redis CLI | `redis-cli` |
| Restart app | `sudo systemctl restart orthoglobe` |
| Reload Caddy | `sudo systemctl reload caddy` |
| Show certs | `sudo caddy list-certificates --config /etc/caddy/Caddyfile` |

---

## Nightly Postgres backup (recommended)

```bash
sudo tee /etc/cron.daily/og-pgdump <<'CRON'
#!/usr/bin/env bash
set -euo pipefail
DEST=/home/deploy/backups
mkdir -p "$DEST"
STAMP=$(date +%Y-%m-%d)
sudo -u postgres pg_dump orthoglobe | gzip > "$DEST/orthoglobe-$STAMP.sql.gz"
find "$DEST" -name 'orthoglobe-*.sql.gz' -mtime +14 -delete
CRON
sudo chmod +x /etc/cron.daily/og-pgdump
```

---

## What's not wired

- **Stripe payments** — orders sit in `pending_invoice`; admin issues
  invoices manually.
- **Off-box backups** — add rsync to B2/S3/Storage Box in the cron.
- **Monitoring** — add UptimeRobot pinging `/admin/health` behind basic
  auth, or install `netdata` (`sudo apt install netdata`).
