DEV Community

ivanpetrus
ivanpetrus

Posted on

Deploy a Node/TypeScript API to Railway in 15 minutes (with a worker-friendly setup)

This walkthrough deploys a simple Express API to Railway, sets env vars, and notes how to add a second service later for a queue worker.

Disclosure: Some links are affiliate links. I may earn a commission at no extra cost to you.

What you'll end up with

  • A deployed Node/TS HTTP service
  • HTTPS URL on Railway
  • Env-based config (PORT, DATABASE_URL, etc.)
  • A clear path to add a worker process (BullMQ etc.) as a second service

Companion repo

Clone or fork the starter and deploy it as-is:

github.com/ivanpetrus/railway-node-ts-api

Prerequisites

  • Node 20+ locally
  • A GitHub repo (or Railway CLI)
  • ~15 minutes

Step 1 — Minimal API

// src/index.ts
import express from "express";

const app = express();
const port = process.env.PORT || 3000;

app.get("/health", (_req, res) => {
  res.json({ ok: true });
});

app.listen(port, () => {
  console.log(`listening on ${port}`);
});
Enter fullscreen mode Exit fullscreen mode

package.json scripts:

{
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsx src/index.ts"
  }
}
Enter fullscreen mode Exit fullscreen mode

Tip: Railway sets PORT for you — don't hardcode 3000 in production.

Step 2 — Create the Railway project

  1. Sign up / log in via this link (includes starter credits when eligible): Railway
  2. New Project → Deploy from GitHub (connect the repo) Or deploy the companion repo above.
  3. Pick the repo + root directory if monorepo

Step 3 — Build & start settings

Step 4 — Environment variables

Add at least:

  • NODE_ENV=production
  • any secrets your app needs (DATABASE_URL, JWT_SECRET, …)

Never commit .env. Rotate anything you pasted into chat.

Step 5 — Public URL & health check

Generate a Railway domain (or custom domain).
Hit /health — you should see { "ok": true }.

Optional: add a worker later

For BullMQ / background jobs:

  • Create a second Railway service from the same repo
  • Same build, different start command (e.g. node dist/worker.js)
  • Share env vars; put Redis as a plugin/add-on or external Redis
  • Cap concurrency so one spike doesn't OOM the service

That's where a lot of solo apps get burned — happy to write a follow-up.

Cost mindset (solo)

  • Start tiny; watch the usage dashboard after 24h
  • Separate web vs worker so a stuck job doesn't take down HTTP
  • Set memory limits / restart policy before you "optimize code"

When Railway is a fit

  • Solo/small team, Node/TS API + optional worker
  • Want GitHub → deploy without babysitting VMs
  • Not a fit if you need multi-region edge at Google scale on day one

Wrap-up

Ship the healthcheck first. Add the worker second. Keep env vars boring.

Deploy link (affiliate): Railway

If you want the same app on a classic VPS/PaaS instead, next post compares Railway vs DigitalOcean for a solo Node API.

Questions / stuck on build commands? Drop an error log snippet in the comments.
In the service settings:

  • Build command: npm run build (or pnpm/yarn)
  • Start command: npm run start
  • Watch the first deploy logs for listening on …

If the build fails on TypeScript, ensure typescript is in dependencies/devDependencies and tsconfig emits to dist/.

Top comments (0)