Deployment guide
The live trial uses Vercel as the public entry point and Render for FastAPI and PostgreSQL. Local Docker remains for development; the hosted site does not depend on the local computer.
Vercel + Render trial
- GitHub:
r3dlabs/open-emotions, branchmain, contains source and canonical YAML data. - Render: the root
render.yamlprovisions the free Docker service and free PostgreSQL database in Singapore. Database access is private to Render. The connection string is injected by the Blueprint, never committed. - Render runs
python -m scripts.serve, which migrates and imports the validated dataset before starting Uvicorn. Keep this trial to one instance; move migrations to a serialized release job before scaling. - Vercel: set Root Directory to
deploy/vercel, Framework Preset to Other, and disable including files outside the root directory. The committedvercel.jsonrunsnode build.mjs, which emits Vercel Build Output API routes forwarding all paths tohttps://openemotions.onrender.com. No Python or database credentials are needed on Vercel. - Public URL:
https://open-emotions.vercel.app. Render'sSITE_URLandCORS_ORIGINSuse that URL. Update them when adding a custom domain.
Free Render web services sleep after inactivity, so initial requests can be slow. Free PostgreSQL expires after 30 days; upgrade or migrate before expiry. The YAML dataset remains in GitHub and can rebuild the database. See Render free limits.
GitHub setup
Create the actual canonical repository under the appropriate owner. From this directory:
git init -b main
git add .
git commit -m 'Prepare OpenEmotions v0.1'
git remote add origin <actual-repository-url>
git push -u origin main
Replace the bracketed remote with the real URL. Enable Actions, branch protection, required CI, and private vulnerability reporting. Assign CODEOWNERS to real maintainers. Set GITHUB_URL to that repository in the host's configuration; the contribution page will display its GitHub link. Add actual maintainer and privacy contact information before launch.
PostgreSQL / Supabase
Create a project, keep the database password in the host's secret store, and copy the connection string from its Connect dialog. For a persistent backend, use a direct connection where networking supports it or the session pooler on IPv4. For migration/backup jobs, prefer the direct connection. Do not guess pooler hostnames. Require SSL, for example by appending ?sslmode=require to the supplied connection string.
The app accepts postgresql:// and rewrites it for psycopg. Native postgresql+psycopg:// is also supported. Percent-encode reserved password characters. Never put the database connection in browser code. Supabase's connection guide explains current modes and networking.
Use a private database schema or prevent the Supabase Data API from exposing the projection. Use separate migration/import and SELECT-only runtime roles, with only CONNECT/USAGE/SELECT privileges for the latter. Configure this with actual role/schema names and verify anon/authenticated cannot query project tables through Supabase. Credentials and policies are operator-specific and intentionally not fabricated here.
Render or compatible container host
The included render.yaml is a Blueprint for one Docker web service and its PostgreSQL database. It sets APP_ENV=production, the public SITE_URL, the actual GITHUB_URL, and exact CORS_ORIGINS. The service uses /health and prepares schema/data before starting the web process because free services do not support pre-deploy jobs. On an always-on paid deployment, move initialization to a controlled pre-deploy job:
python -m scripts.validate_data
python -m alembic upgrade head
python -m scripts.import_data
python -m uvicorn app.main:app --host 0.0.0.0 --port 8000
For strict separate DB roles, run migration/import with the privileged credential and launch the web service with the read-only credential. The simple Blueprint uses one supplied URL for initial setup; harden privileges before public operation. Review current Render Blueprint settings when provisioning.
Expose only the web service through TLS termination. Trust forwarded headers only from the actual ingress; never use unrestricted proxy trust on an internet-reachable origin. Apply distributed limits at the edge for multiple workers or replicas. Configure Uvicorn/proxy log retention, access controls and query logging deliberately. Secrets belong in the platform's environment settings.
Domains and verification
Attach www.open-emotions.com and api.open-emotions.com as custom domains on the same service. Create the exact DNS records returned by the host and wait for certificate verification. Do not invent a CNAME target or IP. The simplest human documentation location is /documentation; /docs remains Swagger UI. SITE_URL sets canonical links and sitemap origin.
curl https://api.open-emotions.com/health
curl https://api.open-emotions.com/v1/meta
curl https://api.open-emotions.com/v1/emotions/anger
Check metadata digest, homepage, explorer filters, /openapi.json, downloads and HTTPS. Restrict CORS to the desired origins. Basic app rate limiting is not a substitute for ingress controls.
Rollbacks and backups
Take a database backup before schema changes. Retain the previous image, tag and canonical dataset export. Prefer additive schema changes that remain compatible while instances roll. For rollback, redeploy a compatible prior image and import its validated snapshot; restore the database backup if required. Recheck metadata after rollback. The database is reconstructible from Git but operational backups aid recovery.
Optional Neo4j
Create an AuraDB or compatible Neo4j database, install the graph extra, and set NEO4J_URI (normally neo4j+s://...), NEO4J_USERNAME, and NEO4J_PASSWORD in the sync job's environment. Run the exporter/dry run before python -m scripts.sync_graph --apply. The script creates a uniqueness constraint and synchronizes only OpenEmotions-owned content. Run one graph writer at a time. No graph credentials are needed by the API.