Files
podcastdistributiona/deploy/README.md
T
2026-06-07 03:58:32 -04:00

3.5 KiB

Deploying PodcastYes on Plesk (Linux)

Single-VPS deployment: one Next.js web process + one worker process under PM2, an external Postgres, local-disk storage, and no Redis.

0. Prerequisites on the VPS

# Node 20+ (Plesk → Tools & Settings → Node.js, or nvm)
node -v            # >= 20

# ffmpeg (the worker shells out to it for audio stitching)
sudo apt update && sudo apt install -y ffmpeg
ffmpeg -version

# PM2 (run outside Plesk's Passenger/Node extension)
sudo npm install -g pm2

1. Database

Create the database on your external Postgres host and put its URL in .env as DATABASE_URL. pg-boss auto-creates its own pgboss schema on first run.

2. Code + env

cd /var/www/vhosts/<your-domain>          # your vhost docroot
git clone <repo> app && cd app            # or Plesk Git deployment
cp .env.example .env                       # then fill in real values
npm ci

Fill in .env: DATABASE_URL, BETTER_AUTH_SECRET (openssl rand -base64 32), BETTER_AUTH_URL / NEXT_PUBLIC_APP_URL (your https domain), OPENAI_API_KEY, ELEVENLABS_API_KEY, STORAGE_DIR (absolute, e.g. /var/www/vhosts/<your-domain>/storage), Stripe + PayPal keys, RESEND_API_KEY.

3. Migrate, seed, build

npx prisma migrate deploy     # create all tables
npm run db:seed               # populate the Plan catalog
npm run build                 # next build (standalone) + postbuild asset copy
mkdir -p "$STORAGE_DIR"/{mp3,art,exports}

4. Run under PM2

pm2 start ecosystem.config.js   # starts podcastyes-web + podcastyes-worker
pm2 save                        # remember the process list
pm2 startup                     # print the systemd command to run once (resurrect on reboot)
pm2 logs                        # tail logs

The web process listens on 127.0.0.1:3000; the worker consumes the pg-boss queue.

5. nginx (reverse proxy + public art)

Plesk → Domains → your domain → Apache & nginx SettingsAdditional nginx directives. Paste deploy/nginx-podcastyes.conf (replace PODCASTYES_DOMAIN and the storage path). proxy_buffering off is required for the live progress stream (SSE).

6. SSL

Plesk → SSL/TLS Certificates → install Let's Encrypt for the domain. Make sure BETTER_AUTH_URL / NEXT_PUBLIC_APP_URL use https://.

7. Webhooks

  • Stripe: Dashboard → Developers → Webhooks → add endpoint https://<domain>/api/webhooks/stripe, events checkout.session.completed, customer.subscription.created/updated/deleted. Put the signing secret in STRIPE_WEBHOOK_SECRET. Create the 3 paid Prices and set the STRIPE_PRICE_* env vars.
  • PayPal: create a subscription Product + Plans (Creator/Pro/Agency), set PAYPAL_PLAN_*. Add a webhook to https://<domain>/api/webhooks/paypal for the BILLING.SUBSCRIPTION.* events and set PAYPAL_WEBHOOK_ID.

8. First admin

# After signing up in the app once:
npm run make-admin you@email.com

Then visit /admin.

9. Redeploys (zero-downtime)

git pull
npm ci
npx prisma migrate deploy
npm run build
pm2 reload ecosystem.config.js

Backups

STORAGE_DIR (generated MP3s + art) is the only state not in Postgres — add it to a nightly offsite backup. The DB is your external Postgres provider's responsibility.

Scaling later (optional)

  • Move the worker to a second VPS pointing at the same DATABASE_URL — change nothing in code.
  • Swap local disk for S3/R2 by adding lib/storage/s3.ts and switching the registry in lib/storage/index.ts.