Railway Deployment Guide: Everything You Need to Know for Node.js and Python Apps
A comprehensive guide to deploying Node.js and Python applications on Railway, including PostgreSQL, environment variables, and scaling.
Why Railway?
After deploying 8+ production applications on Railway, it has become our go-to platform. Railway sits in the sweet spot between Heroku's simplicity and AWS's flexibility — you get easy deploys with enough control for production workloads.
Getting Started
Railway detects your project type automatically. Push to GitHub, connect the repo, and Railway handles the rest. But there are nuances worth knowing.
Node.js Projects
Railway uses Nixpacks to build Node.js apps. It detects package.json and runs npm ci followed by npm start. Customize this with a Procfile or Railway-specific config:
// package.json
{
"scripts": {
"start": "node server.js",
"build": "npm run compile"
},
"engines": {
"node": "20.x"
}
}
Python Projects
For Python apps, Railway detects requirements.txt or Pipfile. Specify your start command in a Procfile:
web: gunicorn -w 2 -b 0.0.0.0:$PORT app:app
Important: Always bind to 0.0.0.0 and use the $PORT environment variable. Railway assigns a dynamic port.
PostgreSQL Setup
Adding PostgreSQL is one click in the Railway dashboard. The DATABASE_URL environment variable is automatically injected. Use it directly:
// Node.js with pg
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: false }
});
# Python with psycopg2
import os
import psycopg2
conn = psycopg2.connect(os.environ['DATABASE_URL'])
Database Migrations
Run migrations as part of your deploy:
// package.json
{
"scripts": {
"start": "node server.js",
"prestart": "npx knex migrate:latest"
}
}
Or use Railway's deploy command:
// Procfile
release: npx knex migrate:latest
web: node server.js
Environment Variables
Railway has three layers of environment variables:
- Service variables — specific to one service
- Shared variables — accessible by multiple services
- Railway-provided —
PORT,DATABASE_URL,RAILWAY_ENVIRONMENT, etc.
Best Practices
- Never hardcode secrets in your repo
- Use Railway's variable references:
${{Postgres.DATABASE_URL}} - Set different values per environment (production vs. staging)
Custom Domains
Railway provides a .railway.app subdomain by default. For custom domains:
- Add your domain in Railway's settings
- Create a CNAME record pointing to your Railway domain
- Railway automatically provisions an SSL certificate
Scaling and Performance
Vertical Scaling
Railway charges based on usage (vCPU and memory). For most web apps, the defaults are fine. For AI/ML workloads like image processing, increase memory:
- Standard web apps: 512MB-1GB RAM
- Image processing: 2-4GB RAM
- Background workers: 256MB-512MB RAM
Horizontal Scaling
Railway supports multiple replicas of the same service. Use this with stateless applications:
- Ensure your app doesn't store state in memory
- Use PostgreSQL or Redis for shared state
- Configure health checks so Railway knows when a replica is ready
Cron Jobs and Workers
For scheduled tasks, Railway supports cron services:
// worker.js
const cron = require('node-cron');
cron.schedule('*/15 * * * *', async () => {
console.log('Running inventory sync...');
await syncInventory();
});
Deploy this as a separate service in the same project, sharing the database.
Monitoring and Logs
Railway provides real-time logs in the dashboard. For production apps, also consider:
- Structured logging with JSON output for easier parsing
- Health check endpoints that Railway pings to verify uptime
- Error tracking via Sentry or similar services
app.get('/health', async (req, res) => {
try {
await pool.query('SELECT 1');
res.json({ status: 'healthy', db: 'connected' });
} catch (err) {
res.status(503).json({ status: 'unhealthy', db: 'disconnected' });
}
});
Cost Optimization
Railway's usage-based pricing means you pay for what you use:
- Sleep inactive services — development and staging environments don't need to run 24/7
- Right-size your containers — don't allocate 4GB RAM for a simple API
- Use shared databases — multiple services can share one PostgreSQL instance
- Typical cost: $5-20/month for most small to medium web applications
Conclusion
Railway removes the infrastructure overhead that slows down small teams. With automatic builds, managed PostgreSQL, and sensible defaults, you can go from code to production in minutes. After deploying everything from Flask dashboards to Node.js APIs on Railway, it remains the best platform for indie developers and small teams.