1
Deployments
Kemal Yaylali edited this page 2026-09-12 13:44:49 +01:00

Deployments

Three ways to run it. Pick on cost and on whether you need the pipeline.

Pipeline runs? Idle cost Effort
GCP serverless yes ~£1/month terraform apply
GCP Kubernetes yes control plane + Cloud SQL flag, then destroy
Railway no $5/month flat railway up

GCP serverless (default)

cd infra/terraform
terraform init -backend-config="bucket=<tfstate bucket>"
export TF_VAR_database_url='postgresql+asyncpg://user:pass@host/db?sslmode=require'
terraform apply -var project=<project id>
cd ../.. && make serverless-deploy PROJECT=<project id> TAG=<commit sha>

terraform output web_url is the address to share: it serves the UI and proxies /api, so there is one public address and no CORS. Upload the VEP cache to gs://<project>-rarelens-data/refs/vep before a real annotation, and set -var model_uri=gs://.../models/pathogenicity/1 to score without an MLflow server.

Set a billing budget first. The demo has no authentication.

Railway

Three services — Postgres, api, web — with the API on the private network only, so the UI's /api proxy is the single public entry point.

railway link --project <id> --environment production --service api
cd api && railway up --service api     # the repo is on Gitea, so deploy from the working copy
cd ../web && railway up --service web

Variables: on the API, DATABASE_URL pointing at postgres.railway.internal and DATABASE_IDLE_CONNECTIONS=false; on the UI, API_INTERNAL_URL=http://api.railway.internal:8000, PUBLIC_PIPELINE_ENABLED=false, BASIC_AUTH_USER and BASIC_AUTH_PASSWORD.

Three things to know:

  • The pipeline cannot run there. Nextflow shells out to docker run for VEP and bcftools, and a container platform gives you a container, not a Docker daemon. Cases are annotated elsewhere and copied up with scripts/seed-remote.sh; PUBLIC_PIPELINE_ENABLED=false hides the buttons that would otherwise be left to fail.
  • DATABASE_IDLE_CONNECTIONS=false is what makes sleeping work. Railway decides a service is idle from its outbound traffic, and a pooled database connection is outbound traffic, so the default pool keeps the service awake and billable for ever. It uses NullPool instead — a connection per request, which is nothing at demo traffic and the wrong trade under real load.
  • Serverless must be enabled per service and only applies on the next deploy. Leave it off for Postgres, which holds the volume. Expect the documented 502 on the first request to a slept service; it answers on retry.

Seeding a remote database

scripts/seed-remote.sh "postgresql://user:pass@host:port/db"

Copies the analysed cases and the HPO reference data. --data-only: the target's schema comes from Alembic, so the two cannot disagree about it. Run alembic upgrade head against the target first.