Deploy to DigitalOcean¶
DigitalOcean App Platform runs all three pieces in one app: the FastAPI container, a managed PostgreSQL, and the static app + landing. (Prefer a single server you control? See Self-Hosting for a droplet + Docker Compose.)
Outcome: API at api.yourdomain.com, app at app.yourdomain.com, landing at yourdomain.com.
1. Database¶
Create a Managed PostgreSQL database (Databases → Create) and copy its connection string for FS_DB_URL.
2. Backend (API)¶
- Create an App from your repo and add a Service built from
backend/Dockerfile. - Set environment variables (Configuration):
FS_DB_URL: the managed database connection stringFS_ENVIRONMENT=prod,FS_BASE_API_URL=https://api.yourdomain.com,FS_BASE_WEB_URL=https://app.yourdomain.comFS_JWT_SECRET_KEY,FS_CRON_SECRET, plus Stripe/email keys
- Run migrations against the managed DB:
./sqitch.sh prod deploy(from a console or your machine, with the prodFS_DB_URL). - Add the domain
api.yourdomain.comto the service.
3. App + landing (static sites)¶
Add two Static Site components from the same repo:
- frontend (
frontend/): buildnpm run build; envVITE_API_BASE_URL=https://api.yourdomain.com; domainapp.yourdomain.com. - landing (
landing/): buildnpm run build; domainyourdomain.com.
Serving the app from a sub-path¶
To serve the app at yourdomain.com/app instead of app.yourdomain.com: App Platform routes components of one app by path. Put both static sites in the same app, set the frontend component's Route to /app (the landing keeps /), and set index.html as the frontend's catchall document. The frontend and backend settings that go with this are in Serving from a Sub-Path.
4. Wire it together¶
Point DNS at the App Platform domains, confirm the FS_BASE_* URLs match the live ones, add the Stripe webhook at https://api.yourdomain.com/webhooks/stripe (Billing & Subscriptions), and run the Security checklist.
Next steps¶
Security headers¶
App Platform Static Sites cannot set custom response headers, so the app's security headers are added at the CDN instead. Put the app behind Cloudflare (free) and set them there:
- Add
app.yourdomain.comto a Cloudflare zone and point its DNS at the App Platform domain (proxied). -
In Cloudflare, go to Rules → Transform Rules → Modify Response Header and add one Set rule per header:
Content-Security-Policy: default-src 'self'; connect-src 'self' https://api.yourdomain.com; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; font-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none' X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), microphone=(), geolocation=() -
Enable HSTS under SSL/TLS → Edge Certificates → HTTP Strict Transport Security (HSTS) rather than as a header rule.
Replace https://api.yourdomain.com in connect-src with your real API URL, or the browser will block the app from calling it. See Security.