2026-06-07 18:30:53 -04:00
# Deploying PodcastYes on Dokploy
2026-06-07 03:58:32 -04:00
2026-06-07 18:30:53 -04:00
Containerized deploy: one image, two services (`web` + `worker` ) via Docker
Compose, a persistent volume for generated assets, an **external ** Postgres, and
**no Redis ** . Dokploy's bundled Traefik handles the domain, TLS, and SSE streaming.
2026-06-07 03:58:32 -04:00
2026-06-07 18:30:53 -04:00
Artifacts: [`Dockerfile` ](../Dockerfile ), [`docker-compose.yml` ](../docker-compose.yml ), [`.dockerignore` ](../.dockerignore ).
2026-06-07 03:58:32 -04:00
2026-06-07 18:30:53 -04:00
## 1. Create the app in Dokploy
- New Project → **Create Service → Compose ** .
- **Provider:** Git → `https://git.serfaty.co/admin/podcastyes` , branch `main` .
- **Compose path:** `docker-compose.yml` .
2026-06-07 03:58:32 -04:00
2026-06-07 18:30:53 -04:00
## 2. Environment (Dokploy → Environment tab)
These feed both the build args (`NEXT_PUBLIC_*` ) and runtime env. Minimum:
2026-06-07 03:58:32 -04:00
```
2026-06-07 18:30:53 -04:00
DATABASE_URL=postgresql://user:pass@host:5432/db?schema=public
BETTER_AUTH_SECRET=<openssl rand -base64 32>
BETTER_AUTH_URL=https://your-domain
NEXT_PUBLIC_APP_URL=https://your-domain
OPENAI_API_KEY=...
ELEVENLABS_API_KEY=...
RESEND_API_KEY=... # optional (emails)
GOOGLE_CLIENT_ID=... # optional (OAuth)
GOOGLE_CLIENT_SECRET=...
# Billing (optional until you sell)
STRIPE_SECRET_KEY=...
STRIPE_WEBHOOK_SECRET=...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=...
STRIPE_PRICE_CREATOR_MONTHLY=... # + PRO/AGENCY, _YEARLY
PAYPAL_CLIENT_ID=...
PAYPAL_CLIENT_SECRET=...
PAYPAL_WEBHOOK_ID=...
PAYPAL_API_BASE=https://api-m.paypal.com
PAYPAL_PLAN_CREATOR=... # + PRO/AGENCY
2026-06-07 03:58:32 -04:00
```
2026-06-07 18:30:53 -04:00
`STORAGE_DIR` and `PORT` are set in the compose file — don't override.
2026-06-07 03:58:32 -04:00
2026-06-07 18:30:53 -04:00
## 3. Domain + SSL
Dokploy → the **web ** service → **Domains ** → add your domain, container port **3000 ** , enable HTTPS (Let's Encrypt). Traefik streams responses, so the episode-generation SSE works with no extra config.
2026-06-07 03:58:32 -04:00
2026-06-07 18:30:53 -04:00
## 4. Deploy
Hit **Deploy ** . The build installs deps + ffmpeg, runs `next build` , and starts:
- `web` runs `prisma migrate deploy` (applies all migrations) then `next start` .
- `worker` runs the pg-boss consumer (ffmpeg available in-image).
Both share the `storage` volume at `/app/storage` (mp3/art/exports). Add a volume backup schedule in Dokploy.
2026-06-07 03:58:32 -04:00
2026-06-07 18:30:53 -04:00
## 5. First admin
Dokploy → web service → **Terminal ** (or `docker exec` ):
2026-06-07 03:58:32 -04:00
``` bash
2026-06-07 18:30:53 -04:00
npx tsx scripts/make-admin.ts you@email.com
2026-06-07 03:58:32 -04:00
```
Then visit `/admin` .
2026-06-07 18:30:53 -04:00
## 6. Webhooks
- Stripe → `https://your-domain/api/webhooks/stripe` (events `checkout.session.completed` , `customer.subscription.created/updated/deleted` ); set `STRIPE_WEBHOOK_SECRET` + the `STRIPE_PRICE_*` .
- PayPal → `https://your-domain/api/webhooks/paypal` (`BILLING.SUBSCRIPTION.*` ); set `PAYPAL_WEBHOOK_ID` + `PAYPAL_PLAN_*` .
2026-06-07 03:58:32 -04:00
2026-06-07 18:30:53 -04:00
## Redeploys
Push to `main` → Dokploy auto-deploys (enable the Git webhook), or click **Redeploy ** . Migrations run automatically on each `web` boot.
2026-06-07 03:58:32 -04:00
2026-06-07 18:30:53 -04:00
## Notes
- **Assets** are served by the app (authed `/api/assets/...` , public `/api/public/episodes/[shareId]/...` ) — no separate static route needed.
- **Scaling:** the `worker` can be scaled or moved to its own Compose/host pointing at the same `DATABASE_URL` ; swap local disk for S3/R2 by adding `lib/storage/s3.ts` and switching the registry in `lib/storage/index.ts` .
- **Build args:** `NEXT_PUBLIC_*` are baked at image-build time; changing them requires a rebuild (Dokploy does this on deploy).