Railway#
Railway builds the vid2context image from the repository and runs it with a volume for /data. Config: deploy/containers/railway/railway.json. Image details and env vars: docker.md.
Why a separate Dockerfile#
Railway refuses to build a Dockerfile that contains a VOLUME instruction, and mounts its volumes owned by root, while the image runs as the unprivileged node user. So Railway builds deploy/containers/railway/Dockerfile, which deploy/containers/railway/gen-dockerfile.sh generates from the main Dockerfile with three changes: no VOLUME line, the container starts as root, and railway/entrypoint.sh gives /data to node (once) and then drops root with setpriv before starting the server. The server process itself never runs as root. deploy/containers/test.sh fails when the generated file is stale.
Deploy#
Prerequisites: a Railway account and the CLI (npm install -g @railway/cli, or brew install railway).
railway login
railway init --name vid2context # or: railway link, for an existing project
railway up --detach # first upload creates the serviceThen, once per service:
- Config file. In the service's Settings, under Config-as-code, set the path to
/deploy/containers/railway/railway.json. Railway then builds with the Railway Dockerfile, checks/api/v1/healthbefore switching traffic, and restarts on failure. - Volume.
railway volume add --mount-path /data. The config setsrequiredMountPath: /data, so a deploy without the volume fails instead of silently losing data. - Variables. Set
VID2CONTEXT_API_KEYSbefore you add a public domain; without it every route is open:Railway injectsrailway variables --set "VID2CONTEXT_API_KEYS=$(openssl rand -hex 24)" railway variables --set "GROQ_API_KEY=..." # any variable from .env.example, all optionalPORT; the server listens on it. - Domain.
railway domaincreates a*.up.railway.appURL. - Deploy again:
railway up --detach, thencurl https://<your domain>/api/v1/health.
railway up uploads the working directory, skipping what .gitignore lists (so .env and .vid2context/ stay local). To deploy from GitHub instead, connect the repository in the dashboard; the same config file applies.
Notes#
- Railway ignores the Dockerfile
HEALTHCHECK;healthcheckPathinrailway.jsongates each deploy instead (120 s timeout, which covers a cold start). - One volume per service and no replicas with a volume: this is a single-instance deploy. For several instances, keep the library in Postgres (
DATABASE_URL, Railway has a Postgres template) and drop the volume requirement. - Cost: Railway has no permanent free tier. New accounts get a one-time trial credit; after that the Hobby plan bills usage (CPU, memory, volume storage, egress) with a monthly minimum. An idle instance of this image uses roughly 100 MB of memory; ffmpeg and speech-to-text runs add CPU while they last. Check Railway's pricing page for current numbers.
railway.jsonwas validated against Railway's published schema (deploy/containers/test.sh). It has not been deployed from this repository yet: no Railway account was available while it was written.
YouTube#
Railway runs in datacenters, and YouTube blocks many datacenter IPs. Expect metadata to work and captions or downloads to be refused at times. The robust setup is the container on a home machine behind a Cloudflare Tunnel; see docker.md.