Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions deploy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Muzix deploy

`muzix.kcolbchain.com` is a Next.js standalone build behind Caddy on the
shared kcolbchain VPS. There is no GitHub Action and no webhook —
deploys are triggered by SSHing into the VPS and running this script.

## Layout on the VPS

| Path | What |
|------|------|
| `/opt/muzix/` | Git checkout of `kcolbchain/muzix` (the `main` branch is prod) |
| `/opt/muzix/web/.next/standalone/` | Next.js standalone build (`server.js`) — what systemd runs |
| `/opt/muzix/web/.next/standalone/public/` | **Hand-wired** — Next.js does not copy `public/` into standalone output |
| `/opt/muzix/web/.next/standalone/.next/static/` | **Hand-wired** — Next.js does not copy `.next/static` into standalone output. Note: this lives at `standalone/.next/static`, **not** `standalone/web/.next/static` — the source dir-layout is misleading. |
| `/etc/systemd/system/muzix-web.service` | systemd unit — `node /opt/muzix/web/.next/standalone/server.js` on `127.0.0.1:3700` |
| `/etc/caddy/conf.d/muzix.caddy` | Caddy reverse-proxy to `127.0.0.1:3700` |

## Deploy

```bash
ssh user@<vps-host> '/opt/muzix/deploy/deploy.sh'
```

The script:

1. `git fetch && git reset --hard origin/main` in `/opt/muzix`
2. `npm install` + `npm run build` in `/opt/muzix/web`
3. Copies `web/public/` → `web/.next/standalone/public/`
4. Copies `web/.next/static/` → `web/.next/standalone/.next/static/`
5. `systemctl restart muzix-web`
6. Verifies the service is active; on failure prints the last 30 journal lines and exits non-zero

## Why two hand-wired copies?

Next.js standalone output deliberately omits `public/` and
`.next/static/` to keep the standalone tarball minimal — the docs tell
you to copy them in for production. See
https://nextjs.org/docs/app/api-reference/config/next-config-js/output.

The labs (`/mixdown.html`, `/labelton.html`) live in `web/public/`, so
forgetting the public-copy step is how they 404 in prod.

## Adding new sub-routes / static assets

- Static one-pagers under `web/public/` reach prod automatically once
this script runs.
- New Next.js routes under `web/app/` need a fresh `npm run build`,
which the script already does.
61 changes: 61 additions & 0 deletions deploy/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/bin/bash
# =============================================================
# Muzix — prod deploy
# Runs on the VPS that serves muzix.kcolbchain.com (port 3700,
# behind Caddy). Pulls main, rebuilds the Next.js standalone,
# wires in the assets standalone doesn't bundle automatically,
# and restarts the systemd service.
#
# Usage: ssh user@<vps-host> '/opt/muzix/deploy/deploy.sh'
# =============================================================

set -euo pipefail

REPO=/opt/muzix
SERVICE=muzix-web
SUBJECT=muzix.kcolbchain.com
STANDALONE="$REPO/web/.next/standalone"

echo "==> Deploying ${SUBJECT} (origin/main → prod)..."

cd "$REPO"
git fetch origin --quiet
git reset --hard origin/main
HEAD_SHA="$(git rev-parse --short HEAD)"
echo " Source at ${HEAD_SHA}: $(git log -1 --pretty='%s')"

cd "$REPO/web"
echo "==> npm install..."
npm install --no-audit --no-fund --prefer-offline > /dev/null

echo "==> next build..."
npm run build > /dev/null

# Next.js standalone output does NOT copy these by design — the docs
# explicitly tell you to wire them in for production. See:
# https://nextjs.org/docs/app/api-reference/config/next-config-js/output
# Both destinations are RELATIVE TO THE STANDALONE ROOT — NOT under web/.
# server.js does process.chdir(__dirname) where __dirname is the standalone
# root, then looks for `./.next/static` and `./public`. Writing them under
# `standalone/web/.next/static` (a mirror of the source layout) is wrong:
# the homepage will render but every /_next/static/* request will 404,
# silently breaking all styling.
echo "==> wiring public/ and .next/static into standalone..."
rm -rf "$STANDALONE/public"
mkdir -p "$STANDALONE/public"
cp -r "$REPO/web/public/." "$STANDALONE/public/"

rm -rf "$STANDALONE/.next/static"
cp -r "$REPO/web/.next/static" "$STANDALONE/.next/static"

echo "==> systemctl restart ${SERVICE}..."
sudo systemctl restart "$SERVICE"
sleep 2

if systemctl is-active --quiet "$SERVICE"; then
echo "==> Deploy complete at ${HEAD_SHA}. ${SERVICE} is active."
else
echo "!! ${SERVICE} failed to start. Recent logs:"
journalctl -u "$SERVICE" -n 30 --no-pager
exit 1
fi