Railway.app: Why Indie Hackers Are Switching in 2026
Railway's simplicity, pricing, and DX beat Heroku. Real migration patterns, actual errors you'll hit, and when to switch.
TL;DR
Railway is replacing Heroku for indie hackers because: (1) predictable pay-as-you-go pricing with $5/month minimum, (2) native Docker support without buildpacks, (3) zero cold starts on hobby tier, (4) better developer experience. This guide covers real migration patterns and actual console errors you'll encounter.
Why the Migration Wave Happened
When Heroku [discontinued free dynos in November 2022](https://blog.heroku.com/next-chapter), indie hackers lost their primary deployment platform. Railway filled that vacuum because it's philosophically different:
Heroku (pre-2022): Buildpack abstraction, procfile-based, you never touch containers
Railway: Git push → automatic Docker builds (via Nixpacks or custom Dockerfile), you maintain control
The shift matters. Nixpacks 0.5.x (current as of 2026—[verify in official docs](https://nixpacks.com/docs)) auto-detects 25+ languages including Python 3.12, Node.js 20+, and Rust. No buildpack guessing games.
Pricing Reality Check
Railway's pricing model is transparent:
For a Node.js hobby app with 1 instance, 512MB RAM, and a PostgreSQL database, expect $12-18/month. Verify current rates at [Railway pricing](https://railway.app/pricing).
Heroku's equivalent hobby dyno tier (before sunsetting) was $7/month compute + $9/month database minimum = $16. Railway wins on transparency and scale-as-you-grow economics.
Real Migration Patterns
Pattern 1: Heroku → Railway with Procfile
If your Heroku app used a Procfile, Railway needs explicit entrypoints. Heroku inferred from Procfile and language; Railway uses railway.json:
```json { "$schema": "https://railway.app/railway.schema.json", "build": { "builder": "nixpacks" }, "deploy": { "startCommand": "npm run start", "restartPolicyType": "always", "restartPolicyMaxRetries": 5 } } ```
Critical difference: Heroku's web: node server.js becomes explicit in railway.json. Miss this and you'll see:
``` Error: Failed to start application No start command found. Set startCommand in railway.json ```
Pattern 2: Docker-First (Best Practice)
Provide your own Dockerfile. This eliminates Nixpacks guessing:
```dockerfile FROM node:20-alpine
WORKDIR /app
COPY package*.json ./ RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"] ```
Railway detects Dockerfile automatically. No additional config needed. Build time: ~45 seconds for Node apps.
Pattern 3: PostgreSQL Migration
Railway's PostgreSQL plugin (v14-15, verify in dashboard) connects via standard DATABASE_URL environment variable.
Heroku migration:
```bash
1. Export Heroku data
heroku pg:backups:capture --app your-app heroku pg:backups:download --app your-app2. Create Railway PostgreSQL plugin (UI-based)
3. Restore dump
psql $DATABASE_URL < latest.dump ```Common error:
``` psql: error: could not translate host name "ec2-XXX.compute-1.amazonaws.com" to address: Name or service not known ```
This means Railway's database endpoint uses different DNS. Use the DATABASE_URL Railway provides in variables, not Heroku's old endpoint.
Developer Experience Wins
1. Local Development with railway run
```bash railway run npm run dev ```
Injects all Railway environment variables locally. Heroku required heroku local with a .env.local file managed separately. Railway's approach eliminates env drift.
2. Zero Cold Starts on Hobby Tier
Heroku's free dynos slept after 30 minutes inactivity. Railway's $5 minimum tier runs 24/7. Containers stay warm.
3. Native Private Networking
Database and services on the same Railway project communicate via internal DNS without exposing ports publicly:
```javascript const pgUrl = process.env.DATABASE_PRIVATE_URL || process.env.DATABASE_URL; ```
Heroku lacked this; all traffic routed through public endpoints.
Actual Console Errors You'll Hit
Error 1: Build Timeout
``` Build failed: 900s timeout exceeded Nixpacks detected 5000 dependencies, building from scratch ```
Solution: Provide Dockerfile to skip Nixpacks detection. Or use nixpacks.toml to pin dependencies.
Error 2: Port Mismatch
``` H10 error: App crashed (reason: listen EADDRINUSE :::3000) ```
Railway injects $PORT environment variable. Ensure your app listens to it:
```javascript const port = process.env.PORT || 3000; app.listen(port); ```
Heroku required the same; this trips developers migrating without updating code.
Error 3: Missing Dependencies in Production
``` Error: Cannot find module 'dotenv' at Function.Module._resolveFilename (internal/modules/cjs/loader.js:4123:57) ```
You moved dotenv to devDependencies. Railway's production builds skip dev dependencies. Move it back:
```bash npm install dotenv # moves to dependencies ```
Cost Breakdown: Real Example
A Nuxt 3 SPA with Node.js backend + PostgreSQL:
Heroku equivalent (using standard-1x): $50+/month. Railway is 80% cheaper at scale.
When NOT to Use Railway
Recommended Setup for 2026
1. Use [railway.json](https://docs.railway.app/reference/config) with explicit start command
2. Provide Dockerfile—don't rely on Nixpacks
3. Pin Node.js version in package.json engines field
4. Use Railway's PostgreSQL plugin for databases
5. Set up [GitHub auto-deploys](https://docs.railway.app/guides/github) for CI/CD
6. Monitor costs with Railway's dashboard (updates real-time)
See also: [environment variables best practices](/?guide=env-management) and [database migration patterns](/?guide=database-migration).
What am I missing?
Railway's ecosystem evolves monthly. Have you encountered issues migrating from Heroku? Found undocumented gotchas with Railway's container builds, PostgreSQL replication, or pricing edge cases? Please share in comments—accuracy depends on collective experience.
Also: Does your team use Railway in production? What's your actual monthly spend vs. expected?