docs/DEPLOY_RAILWAY.md
Rama main · réplica de lectura del repositorio original.
# Hosting a public demo on Railway
This is how the public demo instance is run. The same image works on any host
that can run a Docker container next to a Postgres database (Fly, Render, a VPS).
## How it fits together
One container does everything: `deploy/railway.Dockerfile` builds the frontend,
then runs Django under gunicorn with WhiteNoise serving the API, the admin, the
collected static files, and the built single-page app. Client-side routes fall
back to `index.html` from Django, so no nginx is needed. Postgres is Railway's
managed database, connected through `DATABASE_URL`.
A second Railway service built from the same image runs on a cron schedule and
executes `python manage.py reset_demo --yes`, which wipes every table and
reseeds the sample users and projects. That keeps a public, writable demo from
filling up with junk.
## Environment variables
Set these on the web service:
| Variable | Value | Why |
|---|---|---|
| `DATABASE_URL` | `${{Postgres.DATABASE_URL}}` | Railway reference to the managed database |
| `DJANGO_SECRET_KEY` | a long random string | required in production |
| `DJANGO_ALLOWED_HOSTS` | your service domain, e.g. `timeline-demo.up.railway.app` | Django host check |
| `DJANGO_CSRF_TRUSTED_ORIGINS` | `https://` + the same domain | admin login behind the proxy |
| `SEED_DEMO` | `1` | seed demo users and projects on first start |
| `REQUIRE_ACCOUNT_APPROVAL` | `0` | let visitors register without admin approval |
| `WEB_CONCURRENCY` | `2` | gunicorn workers; 2 fits a small instance |
| `SITE_URL` | `https://` + the domain | links in account emails (none are sent without SMTP) |
| `RAILWAY_DOCKERFILE_PATH` | `deploy/railway.Dockerfile` | makes Railway build from the demo Dockerfile; without it, `railway up` auto-detects the root `index.html` and deploys a static site instead |
| `VITE_DEMO_BANNER` | the notice text | Railway passes service variables as Docker build args, so this is baked into the frontend bundle |
`DJANGO_DEBUG`, `RUN_COLLECTSTATIC`, `SPA_DIST`, and `PORT` are already handled
by the image and by Railway.
The reset service uses the same variables plus a custom start command,
`python manage.py reset_demo --yes`, and a cron schedule such as `0 */6 * * *`.
`reset_demo` deletes every row in the database, so it has two locks: the `--yes` flag and
the environment variable **`ALLOW_DEMO_RESET=1`**. Set that variable on the reset service
and nowhere else. Without it the command refuses to run, which is what protects a real
deployment from a copied cron line or a command run on the wrong host. A production
instance should never set `ALLOW_DEMO_RESET`, `SEED_DEMO`, or `VITE_DEMO_BANNER`.
## Setting it up with the CLI
```bash
railway login
railway init # new project
railway add --database postgres # managed Postgres
railway up --service web # build and deploy from the repo (railway.json picks the Dockerfile)
railway up --service reset # the reset service too, from the same tree (see below)
railway domain # get a public URL
```
Then set the variables above on the web service (`railway variables --set KEY=VALUE`),
redeploy, and add the reset service from the dashboard with the cron schedule.
**Deploy `web` and `reset` together, every time.** `railway up` deploys one service, so a
change needs two runs, from the same working tree. The reset wipes the database with Django's
`flush`, which truncates the tables *its own code* knows about. If `web` has been deployed with
a migration that adds a table and `reset` has not, Postgres refuses the truncate ("cannot
truncate a table referenced in a foreign key constraint"), the cron run crashes, Railway sends a
"deployment crashed" email every six hours, and the demo is never reset. `reset_demo` now
checks for this before touching anything and says so in its error.
## Cost
A small Django container plus a small Postgres on Railway's Hobby plan runs on
the order of five to ten US dollars a month. Traffic spikes do not change that
much; the instance simply gets slower.
## Abuse
The demo is writable by anyone who signs in. The periodic reset is the main
defence. Do not attach a real mail account to the demo, and do not reuse its
secret key or database anywhere else.