Skip to main content

Deploy Costpilot

The API image serves the built dashboard itself, so one service with a volume hosts everything. Runtime config comes from environment variables (see Environment variables). SQLite lives on the volume: the data volume per team is small, one file needs no managed database, and serverless hosts have no persistent filesystem.

Railway​

One click​

Deploy on Railway

The template creates one service built from apps/api/Dockerfile, a volume mounted at /data, and DEMO_MODE=1. Add a custom domain under Settings → Networking.

Manual setup​

railway.json at the repo root selects the Dockerfile and the /health check.

  1. New project → deploy from the GitHub repo. Keep the root directory at the repo root (the build needs packages/shared and apps/web); leave the start command empty (the Dockerfile has it).
  2. Add a volume and mount it at /data.
  3. Set DEMO_MODE=1 for a demo, or COSTPILOT_PUBLIC_URL and COSTPILOT_INGEST_TOKEN for a team instance (see Secure your instance). PORT, AI_COST_DATA_DIR and COSTPILOT_STATIC_DIR come from Railway and the Dockerfile. CORS_ORIGINS is only needed if a dashboard on another origin should call the API.
  4. Generate a public domain or add a custom one via CNAME. Dashboard and API share it.
  5. Optional, for a public demo: add a cron service that pings the API shortly after midnight UTC, so the daily reseed (a few seconds) happens then and not on a visitor's first request. New service → Docker image curlimages/curl, start command curl -fsS https://<your-domain>/api/meta, cron schedule 5 0 * * * (Settings → Cron Schedule). If the ping ever fails, the first visitor of the day triggers the reseed instead.

Publishing or updating the template​

From a working project: Project → Settings → "Generate Template from Project". In the composer, check the service: the repo on main, root directory empty, volume at /data, variable DEMO_MODE=1. Publish and copy the template code from the resulting railway.com/new/template/<code> URL into the deploy button. The repo must be readable by the people deploying it.

Docker on any host​

docker build -f apps/api/Dockerfile -t costpilot .
docker run -p 8080:8080 -v costpilot-data:/data -e DEMO_MODE=1 costpilot

Mount a volume at /data; that is where the SQLite file and the price cache live.

Fly.io​

apps/api/fly.toml mirrors the Railway setup:

fly launch --no-deploy --copy-config --config apps/api/fly.toml
fly volumes create costpilot_data --size 1
fly deploy --config apps/api/fly.toml --dockerfile apps/api/Dockerfile

Split hosting (optional)​

The dashboard can be deployed separately, for example to Vercel via the root vercel.json. Set VITE_API_URL to the API origin at build time and restrict CORS_ORIGINS in the API to that domain (explicit origins also allow the session cookie). Sign-in then needs both on the same site, e.g. app.example.com and api.example.com, because the cookie is SameSite=Lax.

Upgrading​

Redeploy from the new commit. The existing SQLite file is reused; schema changes are additive, with one exception: an issues table in the old PoC shape (free-text project column) is dropped and recreated. Back up the volume before major upgrades.