Agent skill
Railway Deploy
This skill should be used when the user asks to "deploy to Railway", "deploy backend", "deploy Python app", "deploy Node.js server", "railway deployment", or mentions Railway hosting workflows.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/railway-deploy-chunkytortoise-enterprisehub
SKILL.md
Deploy to Railway
Overview
Railway provides modern backend deployment with automatic builds, environment management, and database integration. This skill guides through production-ready Railway deployments for Python, Node.js, and other backend applications.
Prerequisites Check
Install Railway CLI
# Install via npm
npm install -g @railway/cli
# Install via curl (Unix/macOS)
curl -fsSL https://railway.app/install.sh | sh
# Install via Homebrew (macOS)
brew install railway
Authentication
railway login
Verify Setup
railway --version
railway whoami
Project Configuration
Railway Configuration File
Create railway.json in project root:
{
"deploy": {
"startCommand": "python main.py",
"healthcheckPath": "/health",
"healthcheckTimeout": 300,
"restartPolicyType": "ON_FAILURE",
"restartPolicyMaxRetries": 10
}
}
Dockerfile Configuration
# Dockerfile
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Copy requirements first for better caching
COPY requirements.txt .
# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Expose port
EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Start command
CMD ["python", "main.py"]
Python/FastAPI Configuration
# main.py
import os
import uvicorn
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(
title="Railway API",
description="Production-ready API on Railway",
version="1.0.0"
)
# CORS middleware
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Configure for production
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/health")
async def health_check():
"""Health check endpoint for Railway"""
return {
"status": "healthy",
"service": "railway-api",
"version": "1.0.0"
}
@app.get("/")
async def root():
return {"message": "Hello from Railway!"}
if __name__ == "__main__":
port = int(os.environ.get("PORT", 8000))
uvicorn.run(
"main:app",
host="0.0.0.0",
port=port,
reload=False,
access_log=True
)
Node.js/Express Configuration
// server.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(helmet());
app.use(cors());
app.use(express.json());
// Health check endpoint
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
service: 'railway-api',
version: '1.0.0',
timestamp: new Date().toISOString()
});
});
// Routes
app.get('/', (req, res) => {
res.json({ message: 'Hello from Railway!' });
});
// Error handling
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Something went wrong!' });
});
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server running on port ${PORT}`);
});
Deployment Workflows
Initial Project Setup
# Initialize Railway project
railway init
# Link to existing project
railway link [project-id]
# Create new project
railway new
Environment Configuration
# Set environment variables
railway variables set NODE_ENV=production
railway variables set DATABASE_URL=${{Postgres.DATABASE_URL}}
railway variables set REDIS_URL=${{Redis.REDIS_URL}}
# List environment variables
railway variables
# Load local environment
railway run --service backend python main.py
Database Integration
# Add PostgreSQL database
railway add postgres
# Add Redis cache
railway add redis
# Add MongoDB
railway add mongodb
# Check database status
railway status
Deployment Commands
# Deploy current directory
railway up
# Deploy specific service
railway up --service backend
# Deploy with environment
railway up --environment production
# Deploy from Git
railway up --git-branch main
Environment Management
Multi-Environment Setup
# Create environments
railway environment create staging
railway environment create production
# Switch environments
railway environment use staging
railway environment use production
# List environments
railway environment list
Environment Variables Management
# Service-specific variables
railway variables set --service backend API_KEY=your-key
railway variables set --service worker QUEUE_URL=redis://...
# Environment-specific variables
railway variables set --environment production SECRET_KEY=prod-secret
railway variables set --environment staging SECRET_KEY=staging-secret
# Template variables (shared across services)
railway variables set DATABASE_URL=${{Postgres.DATABASE_URL}}
Secrets Management
# Add sensitive variables
railway variables set --service backend \
JWT_SECRET=$(openssl rand -base64 32)
# Use Railway's secret interpolation
railway variables set API_URL="https://${{RAILWAY_PUBLIC_DOMAIN}}/api"
Service Configuration
Multi-Service Applications
# railway.toml
[build]
builder = "NIXPACKS"
[[services]]
name = "backend"
source = "./backend"
[services.build]
buildCommand = "pip install -r requirements.txt"
startCommand = "python main.py"
[services.deploy]
healthcheckPath = "/health"
healthcheckTimeout = 300
[[services]]
name = "worker"
source = "./worker"
[services.build]
buildCommand = "pip install -r requirements.txt"
startCommand = "python worker.py"
[[services]]
name = "frontend"
source = "./frontend"
[services.build]
buildCommand = "npm run build"
startCommand = "npm start"
Database Services
# PostgreSQL configuration
railway add postgres
railway variables set DATABASE_URL=${{Postgres.DATABASE_URL}}
# Redis configuration
railway add redis
railway variables set REDIS_URL=${{Redis.REDIS_URL}}
# MongoDB configuration
railway add mongodb
railway variables set MONGODB_URI=${{MongoDB.MONGODB_URI}}
CI/CD Integration
GitHub Actions
# .github/workflows/railway-deploy.yml
name: Deploy to Railway
on:
push:
branches: [main, staging]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest
- name: Run tests
run: pytest
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Railway CLI
run: npm install -g @railway/cli
- name: Deploy to Railway
run: railway up --service backend
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
GitLab CI/CD
# .gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
PYTHON_VERSION: "3.11"
test:
stage: test
image: python:$PYTHON_VERSION
script:
- pip install -r requirements.txt
- pip install pytest
- pytest
deploy_staging:
stage: deploy
image: node:18
script:
- npm install -g @railway/cli
- railway up --service backend --environment staging
environment:
name: staging
only:
- staging
deploy_production:
stage: deploy
image: node:18
script:
- npm install -g @railway/cli
- railway up --service backend --environment production
environment:
name: production
only:
- main
when: manual
Monitoring and Logging
Application Logging
# logging_config.py
import logging
import sys
from datetime import datetime
# Configure structured logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout)
]
)
logger = logging.getLogger(__name__)
# Request logging middleware for FastAPI
from fastapi import Request
import time
async def log_requests(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
logger.info(
f"Request: {request.method} {request.url} "
f"Status: {response.status_code} "
f"Time: {process_time:.4f}s"
)
return response
Health Monitoring
# health.py
import psutil
import asyncpg
from fastapi import HTTPException
async def check_database_health():
"""Check database connectivity"""
try:
conn = await asyncpg.connect(DATABASE_URL)
await conn.execute("SELECT 1")
await conn.close()
return True
except Exception as e:
logger.error(f"Database health check failed: {e}")
return False
async def check_system_health():
"""Check system resources"""
cpu_percent = psutil.cpu_percent(interval=1)
memory_percent = psutil.virtual_memory().percent
return {
"cpu_usage": cpu_percent,
"memory_usage": memory_percent,
"healthy": cpu_percent < 80 and memory_percent < 80
}
@app.get("/health/detailed")
async def detailed_health_check():
"""Comprehensive health check"""
db_healthy = await check_database_health()
system_health = await check_system_health()
if not db_healthy or not system_health["healthy"]:
raise HTTPException(status_code=503, detail="Service unhealthy")
return {
"status": "healthy",
"database": db_healthy,
"system": system_health,
"timestamp": datetime.utcnow().isoformat()
}
Railway Logging
# View logs
railway logs
# Follow logs in real-time
railway logs --follow
# Filter logs by service
railway logs --service backend
# Export logs
railway logs --json > app-logs.json
Database Management
PostgreSQL Setup
# database.py
import asyncpg
import asyncio
from contextlib import asynccontextmanager
class DatabaseManager:
def __init__(self, database_url: str):
self.database_url = database_url
self.pool = None
async def connect(self):
"""Create connection pool"""
self.pool = await asyncpg.create_pool(
self.database_url,
min_size=5,
max_size=20,
command_timeout=60
)
async def disconnect(self):
"""Close connection pool"""
if self.pool:
await self.pool.close()
@asynccontextmanager
async def get_connection(self):
"""Get database connection from pool"""
async with self.pool.acquire() as connection:
yield connection
# Migration script
async def run_migrations():
"""Run database migrations"""
db = DatabaseManager(DATABASE_URL)
await db.connect()
async with db.get_connection() as conn:
await conn.execute("""
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
""")
await db.disconnect()
Redis Integration
# redis_client.py
import redis.asyncio as redis
import json
class RedisClient:
def __init__(self, redis_url: str):
self.redis_url = redis_url
self.client = None
async def connect(self):
"""Connect to Redis"""
self.client = redis.from_url(
self.redis_url,
encoding="utf-8",
decode_responses=True
)
async def disconnect(self):
"""Disconnect from Redis"""
if self.client:
await self.client.close()
async def set_cache(self, key: str, value: dict, ttl: int = 3600):
"""Set cache with TTL"""
await self.client.setex(key, ttl, json.dumps(value))
async def get_cache(self, key: str):
"""Get cache value"""
value = await self.client.get(key)
return json.loads(value) if value else None
async def delete_cache(self, key: str):
"""Delete cache key"""
await self.client.delete(key)
Performance Optimization
Application Optimization
# performance.py
from functools import wraps
import time
import asyncio
def async_lru_cache(maxsize: int = 128):
"""Async LRU cache decorator"""
def decorator(func):
cache = {}
cache_order = []
@wraps(func)
async def wrapper(*args, **kwargs):
key = str(args) + str(sorted(kwargs.items()))
if key in cache:
# Move to end (most recently used)
cache_order.remove(key)
cache_order.append(key)
return cache[key]
# Execute function
result = await func(*args, **kwargs)
# Add to cache
cache[key] = result
cache_order.append(key)
# Maintain maxsize
while len(cache) > maxsize:
oldest_key = cache_order.pop(0)
del cache[oldest_key]
return result
return wrapper
return decorator
# Connection pooling
from sqlalchemy.pool import QueuePool
engine = create_async_engine(
DATABASE_URL,
poolclass=QueuePool,
pool_size=10,
max_overflow=20,
pool_recycle=3600,
pool_pre_ping=True
)
Resource Limits
# railway.toml
[deploy]
healthcheckPath = "/health"
healthcheckTimeout = 300
restartPolicyType = "ON_FAILURE"
restartPolicyMaxRetries = 3
[build]
builder = "NIXPACKS"
[environment]
NODE_ENV = "production"
PORT = 8000
# Resource constraints
[resources]
memory = "1Gi"
cpu = "500m"
Security Best Practices
Environment Security
# security.py
import os
from typing import Optional
class SecurityConfig:
"""Security configuration management"""
def __init__(self):
self.secret_key = self._get_required_env("SECRET_KEY")
self.database_url = self._get_required_env("DATABASE_URL")
self.allowed_origins = self._get_env_list("ALLOWED_ORIGINS")
def _get_required_env(self, key: str) -> str:
"""Get required environment variable"""
value = os.getenv(key)
if not value:
raise ValueError(f"Required environment variable {key} not set")
return value
def _get_env_list(self, key: str, default: Optional[str] = None) -> list:
"""Get environment variable as list"""
value = os.getenv(key, default or "")
return [item.strip() for item in value.split(",") if item.strip()]
# CORS configuration
app.add_middleware(
CORSMiddleware,
allow_origins=security_config.allowed_origins,
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["*"],
)
Input Validation
# validation.py
from pydantic import BaseModel, validator
from typing import Optional
import re
class UserCreate(BaseModel):
email: str
password: str
name: Optional[str] = None
@validator('email')
def validate_email(cls, v):
if not re.match(r'^[^@]+@[^@]+\.[^@]+$', v):
raise ValueError('Invalid email format')
return v.lower()
@validator('password')
def validate_password(cls, v):
if len(v) < 8:
raise ValueError('Password must be at least 8 characters')
return v
Troubleshooting Common Issues
Build Failures
# Check build logs
railway logs --service backend
# Debug build locally
railway run --service backend python main.py
# Clear build cache
railway up --service backend --no-cache
Database Connection Issues
# Connection testing
async def test_database_connection():
"""Test database connectivity"""
try:
conn = await asyncpg.connect(DATABASE_URL)
result = await conn.fetchval("SELECT version()")
await conn.close()
print(f"Database connected: {result}")
return True
except Exception as e:
print(f"Database connection failed: {e}")
return False
Performance Issues
# Monitor resource usage
railway metrics --service backend
# Check service status
railway status --service backend
# Scale service
railway up --service backend --replicas 3
Additional Resources
Reference Files
For detailed deployment configurations, consult:
references/railway-configurations.md- Comprehensive Railway configuration examplesreferences/database-setup.md- Database integration and managementreferences/monitoring-setup.md- Monitoring and alerting configuration
Example Files
Working deployment examples in examples/:
examples/fastapi-deployment.py- Complete FastAPI Railway setupexamples/nodejs-express-deployment.js- Node.js Express deploymentexamples/docker-configuration.dockerfile- Production Dockerfile examples
Scripts
Deployment utility scripts in scripts/:
scripts/deploy-production.sh- Automated Railway deploymentscripts/setup-database.py- Database initialization scriptscripts/health-check.py- Comprehensive health monitoring
Deploy robust backend applications with Railway's modern infrastructure and these production-ready configurations.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?