@@ -5,38 +5,80 @@ AWS + Terraform (in [deliberation-infrastructure](https://github.com/Watts-Lab/d
55
66## Requirements
77- Spin up ephemeral containers on-demand (per study, lasting a few hours)
8- - Tear down after study completes and data is saved
8+ - One container per experiment — no load balancing, the single server must handle all participants
9+ - Up to 100+ concurrent participants per study
10+ - Tear down after study completes and data is exported
911- Programmatic creation/destruction (API-driven, not manual)
1012- Multiple concurrent studies on different subdomains
1113- WebSocket support (Empirica requires it)
1214- Container runs on port 3000, needs env vars for external services (Daily, Qualtrics, Etherpad, GitHub)
13- - Data export happens via postFlight (pushes to GitHub) — Tajriba state may not need to survive container
15+ - Data export happens via postFlight (pushes to GitHub) — Tajriba state does not need to survive container
16+ - Researcher or management server signals "study is done"; fallback 12hr TTL auto-teardown
1417
15- ## Platform Options
18+ ## Recommended Approach: Railway + Management Server
19+
20+ ### Why Railway
21+ - Already have familiarity/account
22+ - Full GraphQL API (` https://backboard.railway.com/graphql/v2 ` ) for programmatic service lifecycle
23+ - Docker image deploys from GHCR — no rebuild needed
24+ - WebSocket support out of the box
25+ - Services stay running until explicitly deleted (no unwanted auto-suspend)
26+ - Pro plan supports up to 32GB RAM / 8 vCPU per service
27+ - Per-minute billing; a few-hour study on 2GB/1vCPU costs ~ $0.01-0.05
28+ - Custom subdomains via API, or wildcard domain (` *.studies.yourdomain.com ` )
29+
30+ ### API Flow (per study)
31+
32+ 1 . ** Create service** — ` serviceCreate ` mutation with ` source.image ` pointing to GHCR image
33+ 2 . ** Set env vars** — ` variableUpsert ` for DAILY_APIKEY, QUALTRICS_API_TOKEN, SUBDOMAIN, etc.
34+ 3 . ** Attach domain** — ` customDomainCreate ` for ` study-xyz.yourdomain.com `
35+ 4 . ** Wait for deployment** — service starts automatically after creation
36+ 5 . ** Tear down** — ` serviceDelete ` when researcher signals done or TTL expires
37+
38+ ### Management Server Architecture
39+
40+ ```
41+ [Researcher UI] → [Management Server (always-on Railway service)]
42+ |
43+ ├── Railway GraphQL API (create/delete study services)
44+ ├── SQLite/Postgres (tracks active studies, TTLs, researcher info)
45+ └── Timer/cron (reaps studies past 12hr TTL)
46+ ```
47+
48+ The management server is a single small always-on service in the same Railway project.
49+
50+ ### Sizing Guide
51+ | Study size | Recommended tier | Approx. cost/hr |
52+ | ------------------| -------------------| ------------------|
53+ | < 30 participants | 1GB RAM / shared CPU | ~ $0.005 |
54+ | 30–100 participants | 2GB RAM / 1 vCPU | ~ $0.01 |
55+ | 100+ participants | 4GB RAM / 2 vCPU | ~ $0.03 |
56+
57+ Video load is handled by Daily.co, not the study server — Empirica just manages lightweight JSON state sync over WebSocket.
58+
59+ ### References
60+ - [ Railway API Cookbook] ( https://docs.railway.com/guides/api-cookbook )
61+ - [ Manage Services API] ( https://docs.railway.com/guides/manage-services )
62+ - [ Railway Public API] ( https://docs.railway.com/guides/public-api )
63+ - [ Railway Domains] ( https://docs.railway.com/networking/domains )
64+
65+ ## Other Options Considered
1666
1767### Fly.io Machines API
1868- REST API for create/start/stop/destroy machines
19- - Auto-suspend idle machines, auto-start on request
69+ - Auto-suspend idle machines, auto-start on request (not wanted — we need always-on)
2070- Per-second billing, no cost when stopped
2171- Good CLI for scripting
72+ - Would also work well, but less familiar
2273
2374### AWS ECS Fargate (simplify existing AWS)
2475- Replace Terraform with a thin API calling ` RunTask ` / ` StopTask `
2576- No EC2 management, pay-per-second
2677- Keeps existing container registry (GHCR) and AWS account
78+ - Still more complex than Railway
2779
2880### Google Cloud Run
29- - Push image, API to spin up/tear down services per study
30- - Auto-scales to zero
31- - Simple API
81+ - Auto-scales to zero — not suitable for long-running WebSocket sessions
3282
3383### Modal / Beam
34- - Designed for ephemeral container workloads
35- - Least code to wire up
36- - May have limitations for long-running (multi-hour) WebSocket containers
37-
38- ## Open Questions
39- 1 . Does Tajriba state need to survive the container, or is the GitHub export the source of truth?
40- 2 . How does "study is done" get signaled? Empirica callback, or researcher clicks "shut down"?
41- 3 . How many concurrent studies at peak?
42- 4 . Who requests the server — team researchers or external users needing auth?
84+ - Designed for short-lived workloads — may not support multi-hour WebSocket containers well
0 commit comments