The public demo
A separate deployment with fictional data that anyone can open with Try the demo. It never touches real data: it has its own database and secrets, it's read-only, and its AI features never call a provider (see ADR 0010).
It runs for free: the app on a Render free web service, the database on Neon's free Postgres. A free Render service sleeps after about 15 minutes without visits, and waking it takes up to a minute. The website's demo link shows "Waking up the demo…" and opens it when it's ready, and the app itself keeps retrying with a "Waking up the server" message instead of an error.
1. Neon: the database
- Sign up at neon.tech (free plan) and create a project: name
shortlist-demo, Postgres 17, region AWS Asia Pacific (Singapore) (next to the Render region below). - On the project dashboard, open Connect. Choose the default branch and database, turn Connection pooling off (the app's job queue needs a direct connection), and copy the connection string. It looks like
postgresql://neondb_owner:…@ep-….ap-southeast-1.aws.neon.tech/neondb?sslmode=require.
That's all on Neon. The app creates its tables on first start, and Neon suspends the database when idle (the first request after a while takes a second or two longer).
2. Render: the app
Sign up at render.com with GitHub, and allow it access to the
shortlistrepository.New → Web Service → pick the
shortlistrepo. Then:- Name:
shortlist-app-demo(the public demo runs athttps://shortlist-app-demo.onrender.com; with another name, use the address Render shows you everywhere below) - Region: Singapore · Branch:
main· Language/Runtime: Docker (detected from the Dockerfile) - Instance type: Free
- Name:
Advanced → Docker Command:
node apps/api/dist/scripts/start.jsExactly that, with no
sh -cor quotes (Render runs the field as a plain command). Free instances have no pre-deploy step, sostart.jsmigrates, rebuilds the fictional data (becauseDEMO_MODE=true) and then starts the server, on every start; this also keeps the demo's dates current.Advanced → Health Check Path:
/api/healthEnvironment variables:
Key Value NODE_ENVproductionSERVE_WEBtrueDEMO_MODEtrueSIGNUP_MODEclosedTRUST_PROXY3(requests pass Cloudflare, then Render's balancer)FEATURE_EMAIL_INTAKEfalseDATABASE_POOL_MAX5APP_ORIGINhttps://shortlist-app-demo.onrender.comDATABASE_URLthe Neon connection string from step 1 ENCRYPTION_KEYSdemo:<a new key>; generate one withnode -e "console.log(require('crypto').randomBytes(32).toString('base64'))". Never reuse another instance's key.ENCRYPTION_ACTIVE_KEY_IDdemoSet nothing else: no AI keys, no IMAP, no SMTP.
Create Web Service. The first build takes a few minutes.
The repo also has a render.yaml Blueprint with the same settings (New → Blueprint), if you prefer that to clicking through.
The seed refuses to run on a database holding any non-demo account, so pointing the demo at a real database by mistake fails safely.
3. Check it
https://shortlist-app-demo.onrender.com/api/config→"demo": truehttps://shortlist-app-demo.onrender.com/api/health/request→"protocol": "https", andipis your own public IP (if it shows a10.xor Cloudflare address, adjustTRUST_PROXY)- The page shows Try the demo, and inside, a "Demo · fictional data, read-only" bar.
- Changing a status shows "This is a read-only demo…".
- After 15+ minutes without visits, the website's demo link shows "Waking up the demo…" and then opens it.
If you use a different address, set it in site/config.json (demoUrl) so the website's demo link points at it.
Run it locally
createdb jobtracker_demo # or via docker exec … psql
DATABASE_URL=postgres://…/jobtracker_demo DEMO_MODE=true pnpm db:migrate
DATABASE_URL=postgres://…/jobtracker_demo DEMO_MODE=true pnpm --filter @jt/api demo:seed
Then start the server with the same two variables (plus SERVE_WEB=true after pnpm build).