Cloud Platforms
Deploy to production - Railway, Render, Fly.io, AWS, and more.
5 min read
Choosing a Platform#
| Platform | Best For | Pricing | Complexity |
|---|---|---|---|
| Railway | Quick deploys, full-stack | Free tier, then usage | Low |
| Render | Static + API, managed DBs | Free tier, then fixed | Low |
| Fly.io | Edge deployment, containers | Free tier, then usage | Medium |
| Vercel | Frontend + serverless API | Free tier, then usage | Low |
| AWS | Enterprise, full control | Pay-as-you-go | High |
| DigitalOcean | VPS, App Platform | Fixed + usage | Medium |
Option 1: Railway (Easiest)#
Best for: Full-stack apps, quick deploys, databases included
Setup#
- Connect GitHub repo
- Railway auto-detects Node.js
- Add environment variables
- Deploy
bash
# Or use CLI
npm install -g @railway/cli
railway login
railway init
railway up
railway.json#
json
{
"$schema": "https://railway.app/railway.schema.json",
"build": {
"builder": "NIXPACKS"
},
"deploy": {
"startCommand": "node src/index.js",
"healthcheckPath": "/health",
"restartPolicyType": "ON_FAILURE"
}
}
Add Database#
bash
# In Railway dashboard: Add → Database → MongoDB/PostgreSQL/Redis
# Environment variables auto-injected
Option 2: Render#
Best for: Managed services, predictable pricing
render.yaml#
yaml
services:
- type: web
name: my-api
runtime: node
buildCommand: npm ci
startCommand: node src/index.js
envVars:
- key: NODE_ENV
value: production
- key: MONGODB_URI
fromDatabase:
name: my-db
property: connectionString
databases:
- name: my-db
databaseName: myapp
plan: free # or starter, standard, etc.
Deploy#
bash
# Push to GitHub, Render auto-deploys
# Or use Blueprint (render.yaml)
Option 3: Fly.io (Edge)#
Best for: Global edge deployment, Docker-based
fly.toml#
toml
app = "my-api"
primary_region = "iad"
[build]
dockerfile = "Dockerfile"
[env]
NODE_ENV = "production"
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = true
auto_start_machines = true
[[services]]
internal_port = 3000
protocol = "tcp"
[[services.ports]]
port = 443
handlers = ["tls", "http"]
[[services.tcp_checks]]
interval = "15s"
timeout = "2s"
grace_period = "5s"
Deploy#
bash
# Install Fly CLI
curl -L https://fly.io/install.sh | sh
# Login and deploy
fly auth login
fly launch
fly deploy
# Add secrets
fly secrets set JWT_SECRET=xxx MONGODB_URI=xxx
Option 4: Vercel (Serverless)#
Best for: Frontend with API routes, edge functions
vercel.json#
json
{
"version": 2,
"builds": [
{
"src": "src/index.js",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/api/(.*)",
"dest": "src/index.js"
}
]
}
Serverless Function#
javascript
// api/users.js
export default async function handler(req, res) {
if (req.method === 'GET') {
const users = await getUsers();
return res.json({ data: users });
}
res.status(405).json({ error: 'Method not allowed' });
}
Serverless Considerations
Serverless has cold starts and execution limits. Best for light APIs, not heavy processing or WebSockets.
Option 5: AWS (Enterprise)#
Elastic Beanstalk (Managed)#
yaml
# .elasticbeanstalk/config.yml
branch-defaults:
main:
environment: my-api-prod
global:
application_name: my-api
default_region: us-east-1
bash
# Deploy
eb init
eb create my-api-prod
eb deploy
ECS with Fargate (Containers)#
yaml
# task-definition.json
{
"family": "my-api",
"containerDefinitions": [
{
"name": "api",
"image": "123456789.dkr.ecr.us-east-1.amazonaws.com/my-api:latest",
"portMappings": [
{
"containerPort": 3000,
"protocol": "tcp"
}
],
"environment": [
{ "name": "NODE_ENV", "value": "production" }
],
"secrets": [
{
"name": "JWT_SECRET",
"valueFrom": "arn:aws:secretsmanager:..."
}
]
}
]
}
Lambda (Serverless)#
javascript
// lambda.js
import serverless from 'serverless-http';
import { app } from './app.js';
export const handler = serverless(app);
yaml
# serverless.yml
service: my-api
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
functions:
api:
handler: src/lambda.handler
events:
- http: ANY /
- http: ANY /{proxy+}
Option 6: DigitalOcean#
App Platform#
yaml
# .do/app.yaml
name: my-api
services:
- name: api
source:
git:
branch: main
repo_clone_url: https://github.com/user/my-api.git
run_command: node src/index.js
environment_slug: node-js
instance_count: 1
instance_size_slug: basic-xxs
envs:
- key: NODE_ENV
value: production
- key: MONGODB_URI
value: ${db.CONNECTION_STRING}
databases:
- name: db
engine: MONGODB
production: true
Droplet (VPS)#
bash
# SSH into droplet
ssh root@your-droplet-ip
# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# Clone and setup
git clone https://github.com/user/my-api.git
cd my-api
npm ci --production
npm install -g pm2
# Start with PM2
pm2 start src/index.js --name my-api
pm2 save
pm2 startup
CI/CD with GitHub Actions#
yaml
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install and test
run: |
npm ci
npm test
# Railway
- name: Deploy to Railway
uses: railwayapp/nixpacks-action@v1
with:
railway-token: ${{ secrets.RAILWAY_TOKEN }}
# Or Render
- name: Deploy to Render
run: curl ${{ secrets.RENDER_DEPLOY_HOOK }}
# Or Fly.io
- name: Deploy to Fly
uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
Comparison Table#
| Feature | Railway | Render | Fly.io | Vercel | AWS |
|---|---|---|---|---|---|
| Setup time | Minutes | Minutes | Minutes | Minutes | Hours |
| Auto-scaling | Yes | Yes | Yes | Yes | Configurable |
| Custom domains | Yes | Yes | Yes | Yes | Yes |
| SSL | Auto | Auto | Auto | Auto | Manual/ACM |
| Databases | Built-in | Built-in | Add-ons | External | RDS/DynamoDB |
| Cold starts | No | No | Possible | Yes | Lambda: Yes |
| Pricing | Usage | Fixed | Usage | Usage | Complex |
Key Takeaways#
- Start with Railway or Render - Get running in minutes
- Fly.io for global - Edge deployment, low latency
- AWS for enterprise - Full control, complex setup
- Vercel for serverless - Best with Next.js/frontend
- Always use CI/CD - Automate deployments
Recommendation
Start with Railway or Render. They're the simplest, include databases, and scale with you. Move to AWS when you need more control or have specific compliance requirements.
Continue Learning
Ready to level up your skills?
Explore more guides and tutorials to deepen your understanding and become a better developer.