Repository: Single monorepo (Backend + Admin UI)
Date: January 28, 2026
Approach: Native deployment without Docker containers
Cost: $12-24/mo | Difficulty: Easy | Best for: Phase 2
- ✅ Detects Node.js automatically
- ✅ Auto-builds from Git (no Docker needed)
- ✅ Auto-scaling included
- ✅ SSL certificates automatic
- ✅ Zero DevOps knowledge required
- ✅ Built-in monitoring
- ✅ One-click rollbacks
┌─────────────────────────────────────────────┐
│ DigitalOcean App Platform │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ Static Site │ │ Web Service │ │
│ │ (Admin UI) │ │ (API Gateway) │ │
│ │ Auto-build │ │ Node.js 20 │ │
│ │ from React │ │ Auto-scaling │ │
│ └──────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────┘
↓ ↓
CDN (Auto) Load Balancer (Auto)
1. Create app.yaml in root:
name: flexgate-proxy
region: nyc
# Static Site for Admin UI
static_sites:
- name: admin-ui
github:
repo: tapas100/flexgate-proxy
branch: main
deploy_on_push: true
source_dir: /admin-ui
build_command: npm ci && npm run build
output_dir: build
routes:
- path: /admin
environment_slug: node-js
envs:
- key: REACT_APP_API_URL
value: ${api-gateway.PUBLIC_URL}
# Web Service for API Gateway
services:
- name: api-gateway
github:
repo: tapas100/flexgate-proxy
branch: main
deploy_on_push: true
build_command: npm ci && npm run build
run_command: npm start
environment_slug: node-js
instance_count: 2
instance_size_slug: basic-xs
http_port: 3000
health_check:
http_path: /health/live
initial_delay_seconds: 30
period_seconds: 10
routes:
- path: /
envs:
- key: NODE_ENV
value: production
- key: PORT
value: "3000"
- key: REDIS_URL
scope: RUN_AND_BUILD_TIME
value: ${redis.PRIVATE_URL}
- key: JWT_SECRET
scope: RUN_AND_BUILD_TIME
type: SECRET
value: ${JWT_SECRET}
# Database (Redis for rate limiting)
databases:
- name: redis
engine: REDIS
production: true
cluster_name: flexgate-redis2. Deploy:
# Install doctl CLI
brew install doctl
# Authenticate
doctl auth init
# Create app
doctl apps create --spec app.yaml
# Or use web UI:
# 1. Go to DigitalOcean → Apps
# 2. Connect GitHub repo
# 3. Auto-detects Node.js
# 4. Click "Deploy"3. Update package.json scripts:
{
"scripts": {
"build": "tsc -p tsconfig.build.json",
"start": "node ./dist/bin/www",
"postinstall": "cd admin-ui && npm ci && npm run build && cd .."
}
}Cost Breakdown:
- Basic App: $12/mo (2 containers, 1GB RAM)
- Redis: $15/mo (managed)
- Total: $27/mo (scales automatically)
Cost: Free tier available | Difficulty: Easy | Best for: MVP/Testing
- ✅ Generous free tier
- ✅ Auto-deploy from Git
- ✅ Native monorepo support
- ✅ Zero configuration
- ✅ Preview environments
- ✅ Automatic SSL
1. Create render.yaml:
services:
# Backend API Gateway
- type: web
name: flexgate-api
env: node
region: oregon
plan: starter
buildCommand: npm ci && npm run build
startCommand: npm start
healthCheckPath: /health/live
envVars:
- key: NODE_ENV
value: production
- key: PORT
value: 3000
- key: REDIS_URL
fromDatabase:
name: flexgate-redis
property: connectionString
- key: JWT_SECRET
generateValue: true
sync: false
autoDeploy: true
# Frontend Admin UI
- type: web
name: flexgate-admin
env: static
buildCommand: cd admin-ui && npm ci && npm run build
staticPublishPath: admin-ui/build
routes:
- type: rewrite
source: /*
destination: /index.html
envVars:
- key: REACT_APP_API_URL
value: https://flexgate-api.onrender.com
autoDeploy: true
databases:
- name: flexgate-redis
databaseName: flexgate
plan: starter2. Deploy:
# Just push to GitHub
git push origin main
# Render auto-detects render.yaml and deploysCost:
- Free tier: $0/mo (includes SSL, auto-deploy)
- Starter: $7/mo (better performance)
- Redis: $7/mo (managed)
Cost: $5/mo credit (pay-as-you-go) | Best for: Simplicity
1. Create railway.json:
{
"$schema": "https://railway.app/railway.schema.json",
"build": {
"builder": "NIXPACKS"
},
"deploy": {
"startCommand": "npm start",
"healthcheckPath": "/health/live",
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 10
}
}2. Deploy:
# Install Railway CLI
npm i -g @railway/cli
# Login
railway login
# Initialize
railway init
# Deploy
railway upCost:
- $5/mo credit included
- Pay only for what you use (~$10-20/mo for small app)
Cost: Free tier generous | Best for: Global scale
Admin UI (Vercel Edge) → CDN → Users
↓
API Routes (Serverless Functions)
↓
Backend Logic
1. Create vercel.json:
{
"version": 2,
"builds": [
{
"src": "admin-ui/package.json",
"use": "@vercel/static-build",
"config": {
"distDir": "build"
}
},
{
"src": "app.ts",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/admin/(.*)",
"dest": "/admin-ui/$1"
},
{
"src": "/api/(.*)",
"dest": "/app.ts"
},
{
"src": "/(.*)",
"dest": "/app.ts"
}
],
"env": {
"NODE_ENV": "production"
}
}2. Deploy:
# Install Vercel CLI
npm i -g vercel
# Deploy
vercel
# Production
vercel --prodCost:
- Hobby: Free (perfect for small projects)
- Pro: $20/mo (team features)
Cost: Free tier | Best for: Jamstack approach
Cost: Free tier 3 apps | Best for: Global deployment
- ✅ No Docker required (uses Buildpacks)
- ✅ Global deployment
- ✅ Free tier includes 3 apps
- ✅ Scales to zero
- ✅ Anycast networking
1. Create fly.toml:
app = "flexgate-proxy"
primary_region = "lax"
[build]
builder = "paketobuildpacks/builder:base"
buildpacks = ["gcr.io/paketo-buildpacks/nodejs"]
[env]
NODE_ENV = "production"
PORT = "8080"
[http_service]
internal_port = 8080
force_https = true
auto_stop_machines = true
auto_start_machines = true
min_machines_running = 1
processes = ["app"]
[[services]]
http_checks = []
internal_port = 8080
processes = ["app"]
protocol = "tcp"
script_checks = []
[services.concurrency]
hard_limit = 25
soft_limit = 20
type = "connections"
[[services.ports]]
force_https = true
handlers = ["http"]
port = 80
[[services.ports]]
handlers = ["tls", "http"]
port = 443
[[services.tcp_checks]]
grace_period = "1s"
interval = "15s"
restart_limit = 0
timeout = "2s"
[[vm]]
cpu_kind = "shared"
cpus = 1
memory_mb = 2562. Deploy:
# Install flyctl
brew install flyctl
# Login
fly auth login
# Launch app
fly launch
# Deploy
fly deploy
# Scale
fly scale count 3Cost:
- Free: 3 apps, 256MB RAM
- Paid: $1.94/mo per 256MB
| Platform | Cost/Month | Setup Time | Auto-Scale | Free Tier | Best For |
|---|---|---|---|---|---|
| DigitalOcean App | $27 | 10 min | ✅ | ❌ | Production |
| Render | $14 | 5 min | ✅ | ✅ | MVP |
| Railway | $15 | 3 min | ✅ | $5 credit | Simplicity |
| Vercel | Free-$20 | 2 min | ✅ | ✅ | Global CDN |
| Netlify | Free | 5 min | ✅ | ✅ | Jamstack |
| Fly.io | Free-$10 | 5 min | ✅ | ✅ | Edge compute |
Platform: Render (Free tier)
Why: Zero cost, auto-deploy, preview environments
Setup: 5 minutes
Deploy to Render:
# 1. Create render.yaml (already shown above)
git add render.yaml
git commit -m "feat: Add Render deployment config"
git push origin main
# 2. Go to render.com
# 3. Connect GitHub repo
# 4. Render auto-detects and deploys
# 5. Done!
# URLs:
# - API: https://flexgate-api.onrender.com
# - Admin: https://flexgate-admin.onrender.comPlatform: DigitalOcean App Platform
Why: Reliable, affordable, scales well
Cost: $27/mo
Setup: 10 minutes with app.yaml
Platform: Fly.io (multi-region)
Why: Global edge deployment
Cost: ~$50/mo (3 regions)
Platform: Vercel (Admin UI) + Railway (API)
Why: Best of both worlds
Cost: $20 (Vercel) + $30 (Railway) = $50/mo
cat > render.yaml << 'EOF'
services:
- type: web
name: flexgate-api
env: node
buildCommand: npm ci && npm run build
startCommand: npm start
healthCheckPath: /health/live
envVars:
- key: NODE_ENV
value: production
- type: web
name: flexgate-admin
env: static
buildCommand: cd admin-ui && npm ci && npm run build
staticPublishPath: admin-ui/build
envVars:
- key: REACT_APP_API_URL
value: https://flexgate-api.onrender.com
EOF{
"scripts": {
"build": "tsc -p tsconfig.build.json",
"start": "node ./dist/bin/www",
"deploy": "git push origin main"
},
"engines": {
"node": "20.x",
"npm": "10.x"
}
}git add render.yaml package.json
git commit -m "feat: Add Render deployment configuration"
git push origin main
# Then:
# 1. Go to https://render.com
# 2. Sign up with GitHub
# 3. Click "New" → "Blueprint"
# 4. Select your repo
# 5. Render auto-deploys!# In Render dashboard:
# - Add JWT_SECRET
# - Add any other secrets
# - Click "Deploy"✅ render.yaml
✅ package.json (with engines)✅ app.yaml
✅ package.json✅ railway.json (optional - auto-detects)
✅ package.json✅ vercel.json
✅ package.json✅ fly.toml
✅ Procfile (optional)FlexGate has migrated from GitHub Actions to Jenkins. The full pipeline is defined in
Jenkinsfileat the repository root. GitHub Actions workflows have been removed.
The Jenkins pipeline triggers on every push or merge to main and runs:
Lint → Type Check → Test → Build → Build Admin UI → Publish to npm
For Render deployments, add a Deploy Hook in the Render dashboard and call it from the Jenkins post-build step:
// Add to Jenkinsfile post { success { ... } } block if deploying to Render
sh '''
curl -X POST \
"https://api.render.com/deploy/srv-xxxxx?key=${RENDER_DEPLOY_KEY}"
'''Add RENDER_DEPLOY_KEY as a Jenkins credential (Secret text).
See Jenkinsfile in the repo root and
docs/deployment/DEPLOYMENT_STRATEGY.md for the full
pipeline reference.
{
"scripts": {
"build": "npm ci && tsc && cd admin-ui && npm ci && npm run build",
"start": "node dist/bin/www"
}
}# .env.example
NODE_ENV=production
PORT=3000
REDIS_URL=redis://localhost:6379
JWT_SECRET=your-secret-here
ADMIN_JWT_SECRET=your-admin-secret// Already in app.ts:
app.get('/health/live', (req, res) => {
res.json({ status: 'UP' });
});// Add to bin/www.ts
process.on('SIGTERM', () => {
server.close(() => {
console.log('Server closed');
process.exit(0);
});
});Start with Render (Free):
- ✅ Zero cost to start
- ✅ Auto-deploy from Git
- ✅ Preview environments for PRs
- ✅ Easy scaling when you grow
- ✅ 5-minute setup
Migrate to DigitalOcean App Platform when:
- You have 10+ paying customers
- Need better performance
- Want managed Redis
- Budget allows $27/mo
Scale to Fly.io when:
- 100+ customers globally
- Need edge deployment
- Want multi-region
- Need <100ms latency worldwide
- Create render.yaml (I'll create this for you)
- Update package.json (add engines)
- Push to GitHub
- Connect Render
- Deploy automatically
Want me to create the Render configuration files now?