# 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 ```bash # 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 ```bash cd /var/www/vhosts/ # your vhost docroot git clone 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//storage`), Stripe + PayPal keys, `RESEND_API_KEY`. ## 3. Migrate, seed, build ```bash 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 ```bash 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 Settings** → *Additional 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:///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:///api/webhooks/paypal` for the `BILLING.SUBSCRIPTION.*` events and set `PAYPAL_WEBHOOK_ID`. ## 8. First admin ```bash # After signing up in the app once: npm run make-admin you@email.com ``` Then visit `/admin`. ## 9. Redeploys (zero-downtime) ```bash 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`.