InterviewLab deploys to:
- Railway: Backend API, Agent, Database, Redis
- Vercel: Frontend (Next.js)
graph TB
subgraph Vercel["Vercel"]
FE[Next.js Frontend]
end
subgraph Railway["Railway"]
API[FastAPI Backend]
AGENT[LiveKit Agent]
DB[PostgreSQL]
REDIS[Redis]
end
subgraph External["External"]
LK[LiveKit Cloud]
OPENAI[OpenAI API]
end
FE -->|HTTPS| API
FE -->|WebSocket| LK
API -->|SQL| DB
API -->|Cache| REDIS
AGENT -->|WebSocket| LK
AGENT -->|API| OPENAI
API -->|API| OPENAI
The frontend connects to the backend via HTTPS for REST APIs and directly to LiveKit Cloud via WebSocket for voice streams. The agent runs as a separate Railway service, connecting to LiveKit Cloud to handle voice sessions. Both the API and agent use the same database and Redis instance, sharing connection pools. The agent scales independently—deploy multiple instances and LiveKit distributes connections across them.
- Railway account: railway.app
- GitHub repository connected
- Railway CLI installed:
npm i -g @railway/cli
railway login
railway initPostgreSQL:
railway add postgresql
# Note DATABASE_URL from service variablesRedis:
railway add redis
# Note REDIS_URL from service variablesIn Railway dashboard → Variables:
| Variable | Value | Source |
|---|---|---|
DATABASE_URL |
Auto-set by PostgreSQL service | Railway |
REDIS_URL |
Auto-set by Redis service | Railway |
SECRET_KEY |
Generate: openssl rand -hex 32 |
Manual |
OPENAI_API_KEY |
Your OpenAI API key | Manual |
LIVEKIT_URL |
wss://your-project.livekit.cloud |
LiveKit |
LIVEKIT_API_KEY |
LiveKit API key | LiveKit |
LIVEKIT_API_SECRET |
LiveKit API secret | LiveKit |
ENVIRONMENT |
production |
Manual |
LOG_LEVEL |
INFO |
Manual |
Create railway.json:
{
"$schema": "https://railway.app/railway.schema.json",
"build": {
"builder": "DOCKERFILE",
"dockerfilePath": "Dockerfile"
},
"deploy": {
"startCommand": "uvicorn src.main:app --host 0.0.0.0 --port $PORT",
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 10
}
}Deploy:
railway upSet Public Domain:
- Railway dashboard → Settings → Generate Domain
- Note domain:
your-app.railway.app
Create separate service for agent:
railway service create interview-agent
railway link interview-agentCreate railway-agent.json:
{
"$schema": "https://railway.app/railway.schema.json",
"build": {
"builder": "DOCKERFILE",
"dockerfilePath": "Dockerfile"
},
"deploy": {
"startCommand": "python -m livekit.agents start src.agents.interview_agent",
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 10
}
}Environment Variables (same as API):
- Copy all variables from API service
- Add:
LIVEKIT_AGENT_URL(if using agent URL)
Deploy:
railway upRun migrations on deploy:
railway run alembic upgrade headOr add to startup script:
# In Dockerfile or startup script
alembic upgrade head && uvicorn src.main:app --host 0.0.0.0 --port $PORTRailway automatically checks /health endpoint:
# src/main.py
@app.get("/health")
async def health_check():
return {"status": "healthy"}- Vercel account: vercel.com
- Vercel CLI:
npm i -g vercel
- Go to Vercel dashboard
- Import Git repository
- Select
frontend/as root directory
In Vercel dashboard → Settings → Environment Variables:
| Variable | Value | Environment |
|---|---|---|
NEXT_PUBLIC_API_URL |
https://your-app.railway.app |
Production |
NEXT_PUBLIC_LIVEKIT_URL |
wss://your-project.livekit.cloud |
Production |
Framework Preset: Next.js
Root Directory: frontend
Build Command: npm run build
Output Directory: .next
cd frontend
vercel --prodOr push to main branch (auto-deploy enabled).
- Sign up: livekit.io
- Create project
- Get credentials:
- URL:
wss://your-project.livekit.cloud - API Key
- API Secret
- URL:
Deploy on Railway:
railway service create livekit-server
railway add livekit-serverEnvironment Variables:
LIVEKIT_KEYS="api-key: api-secret"Deploy LiveKit:
FROM livekit/livekit-server
# Configure in railwayFROM python:3.11-slim
WORKDIR /app
# Install dependencies
COPY pyproject.toml ./
RUN pip install --no-cache-dir -r requirements.txt
# Copy source
COPY src/ ./src/
COPY alembic.ini ./
COPY alembic/ ./alembic/
# Run migrations and start
CMD alembic upgrade head && uvicorn src.main:app --host 0.0.0.0 --port $PORTversion: "3.8"
services:
api:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://...
depends_on:
- db
- redis
db:
image: postgres:14
environment:
- POSTGRES_DB=interviewlab
redis:
image: redis:7- CPU Usage: Monitor in Railway dashboard
- Memory Usage: Check service metrics
- Logs: View in Railway dashboard → Logs
Backend:
railway logs
railway logs --service interview-agentFrontend:
- Vercel dashboard → Logs
API Health:
curl https://your-app.railway.app/healthAgent Health:
- Check LiveKit dashboard for agent connections
- Verify agent logs show successful connections
Horizontal Scaling:
- Railway auto-scales based on traffic
- Multiple instances share database/Redis
Resource Limits:
- CPU: 2 vCPU (recommended)
- Memory: 2GB (recommended)
- Disk: 10GB (for logs)
Scaling Agents:
- Deploy multiple agent instances
- LiveKit distributes connections
- Each agent handles 50+ concurrent interviews
Resource Limits:
- CPU: 1 vCPU per agent
- Memory: 1GB per agent
- Consider: Agent count = Expected interviews / 50
PostgreSQL Scaling:
- Start: Railway shared PostgreSQL
- Scale: Dedicated PostgreSQL (higher tier)
- Connection pooling: Use PgBouncer
Redis Scaling:
- Start: Railway Redis (256MB)
- Scale: Upgrade to higher tier
- Use: Caching, session storage
| Issue | Solution |
|---|---|
| Build fails | Check Dockerfile, verify dependencies |
| Database connection error | Verify DATABASE_URL, check PostgreSQL service |
| Agent won't connect | Verify LiveKit credentials, check agent logs |
| Frontend can't reach API | Check CORS settings, verify API URL |
| High memory usage | Enable cleanup, check for memory leaks |
| Slow responses | Check database queries, enable Redis caching |
name: Deploy
on:
push:
branches: [main]
jobs:
deploy-backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: railway-app/railway-action@v1
with:
service: api
token: ${{ secrets.RAILWAY_TOKEN }}
deploy-frontend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: amondnet/vercel-action@v20
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
working-directory: ./frontend- Never commit
.envfiles - Use Railway/Vercel secrets
- Rotate keys regularly
- CORS: Configure allowed origins
- Rate Limiting: Implement per endpoint
- Authentication: JWT tokens required
- SSL: Enable PostgreSQL SSL
- Backups: Enable automatic backups
- Access: Restrict database access
| Service | Tier | Monthly Cost |
|---|---|---|
| Railway (API) | Hobby | $5-20 |
| Railway (Agent) | Hobby | $5-20 |
| Railway (PostgreSQL) | Hobby | $5-10 |
| Railway (Redis) | Hobby | $5-10 |
| Vercel (Frontend) | Hobby | Free |
| LiveKit Cloud | Starter | $0-50 |
| OpenAI API | Pay-as-you-go | $10-100+ |
| Total | $30-220+ |