Skip to main content
This guide covers best practices and considerations for deploying the Horse Trust platform to production.

Pre-Deployment Checklist

Before deploying to production, ensure you have completed the following:
1

Security Audit

Environment variables are properly secured
JWT secrets are strong and unique
CORS origins are restricted to specific domains
Rate limiting is configured
Helmet.js security headers are enabled
2

Database Preparation

MongoDB production cluster is set up
Database backups are configured
Connection pooling is optimized
Database indexes are created
3

Code Quality

All tests are passing
Linting errors are resolved
TypeScript compilation is successful
Dependencies are up to date
4

Performance

Build optimization is complete
Images are optimized
API response times are acceptable
Socket.io performance is tested

Environment Configuration

Production Environment Variables

Never use development settings in production. Update all configuration values.

Server (Backend) Production .env

# ================================
# PRODUCTION SERVER CONFIGURATION
# ================================
PORT=8031
NODE_ENV=production

# ================================
# DATABASE
# ================================
MONGO_URI=mongodb+srv://prod-user:[email protected]/horsetrust_prod?retryWrites=true&w=majority

# ================================
# AUTHENTICATION (Use strong secrets!)
# ================================
JWT_SECRET=GENERATE-STRONG-RANDOM-SECRET-KEY-HERE
JWT_EXPIRES_IN=7d
BCRYPT_SALT_ROUNDS=12

# ================================
# CORS (Specific domains only!)
# ================================
CORS_ORIGINS=https://horsetrust.com,https://www.horsetrust.com,https://app.horsetrust.com

Client (Frontend) Production .env.production

# ==========================================
# PRODUCTION CLIENT CONFIGURATION
# ==========================================

# API URL - Production backend
NEXT_PUBLIC_API_URL=https://api.horsetrust.com/api

# Cloudinary
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=di2agiylz
NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET=horse_trust_uploads

Deployment Options

Option 1: Vercel (Frontend) + Railway/Render (Backend)

Recommended for easy deployment with minimal configuration.

Deploy Frontend to Vercel

1

Install Vercel CLI

npm install -g vercel
2

Navigate to Client Directory

cd client
3

Deploy

vercel --prod
Or connect your GitHub repository through the Vercel Dashboard:
  1. Import your repository
  2. Configure project settings:
    • Framework: Next.js
    • Root Directory: client
    • Build Command: npm run build
    • Output Directory: .next
  3. Add environment variables in Vercel dashboard
  4. Deploy
4

Configure Environment Variables

In Vercel dashboard, add:
  • NEXT_PUBLIC_API_URL
  • NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME
  • NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET

Deploy Backend to Railway

1

Create Railway Account

Sign up at Railway.app
2

Create New Project

  • Connect your GitHub repository
  • Select the server directory as root
3

Configure Build Settings

# railway.json (optional)
{
  "build": {
    "builder": "NIXPACKS",
    "buildCommand": "npm install && npm run build"
  },
  "deploy": {
    "startCommand": "npm start",
    "restartPolicyType": "ON_FAILURE",
    "restartPolicyMaxRetries": 10
  }
}
4

Add Environment Variables

In Railway dashboard, add all server environment variables:
  • PORT (Railway auto-provides this)
  • NODE_ENV=production
  • MONGO_URI
  • JWT_SECRET
  • JWT_EXPIRES_IN
  • BCRYPT_SALT_ROUNDS
  • CORS_ORIGINS
5

Deploy

Railway will automatically deploy on git push to main branch.

Option 2: Docker Deployment

For containerized deployments on any platform.

Server Dockerfile

# server/Dockerfile
FROM node:24-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .
RUN npm run build

# Production image
FROM node:24-alpine

WORKDIR /app

COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./

EXPOSE 8031

CMD ["npm", "start"]

Client Dockerfile

# client/Dockerfile
FROM node:24-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# Production image
FROM node:24-alpine

WORKDIR /app

COPY --from=builder /app/.next ./.next
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/public ./public

EXPOSE 8030

CMD ["npm", "start"]

Docker Compose

# docker-compose.yml
version: '3.8'

services:
  server:
    build:
      context: ./server
      dockerfile: Dockerfile
    ports:
      - "8031:8031"
    environment:
      - NODE_ENV=production
      - PORT=8031
      - MONGO_URI=${MONGO_URI}
      - JWT_SECRET=${JWT_SECRET}
      - JWT_EXPIRES_IN=${JWT_EXPIRES_IN}
      - BCRYPT_SALT_ROUNDS=${BCRYPT_SALT_ROUNDS}
      - CORS_ORIGINS=${CORS_ORIGINS}
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8031/test"]
      interval: 30s
      timeout: 10s
      retries: 3

  client:
    build:
      context: ./client
      dockerfile: Dockerfile
    ports:
      - "8030:8030"
    environment:
      - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
      - NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=${NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}
      - NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET=${NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET}
    depends_on:
      - server
    restart: unless-stopped
Deploy with Docker Compose:
# Build and start services
docker-compose up -d --build

# View logs
docker-compose logs -f

# Stop services
docker-compose down

Option 3: VPS Deployment (Ubuntu/Debian)

For deployment on a virtual private server.
1

Server Setup

# Update system
sudo apt update && sudo apt upgrade -y

# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs

# Install PM2 (Process Manager)
sudo npm install -g pm2

# Install Nginx
sudo apt install -y nginx
2

Clone and Build

# Clone repository
git clone <your-repo-url> /var/www/horsetrust
cd /var/www/horsetrust

# Build server
cd server
npm install
npm run build

# Build client
cd ../client
npm install
npm run build
3

Configure PM2

Create ecosystem.config.js:
module.exports = {
  apps: [
    {
      name: 'horsetrust-server',
      cwd: '/var/www/horsetrust/server',
      script: 'npm',
      args: 'start',
      env: {
        NODE_ENV: 'production',
        PORT: 8031
      }
    },
    {
      name: 'horsetrust-client',
      cwd: '/var/www/horsetrust/client',
      script: 'npm',
      args: 'start',
      env: {
        NODE_ENV: 'production'
      }
    }
  ]
};
Start applications:
pm2 start ecosystem.config.js
pm2 save
pm2 startup
4

Configure Nginx

Create /etc/nginx/sites-available/horsetrust:
# Backend API
server {
    listen 80;
    server_name api.horsetrust.com;
    
    location / {
        proxy_pass http://localhost:8031;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
    # WebSocket support
    location /socket.io/ {
        proxy_pass http://localhost:8031;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

# Frontend
server {
    listen 80;
    server_name horsetrust.com www.horsetrust.com;
    
    location / {
        proxy_pass http://localhost:8030;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
    }
}
Enable and restart:
sudo ln -s /etc/nginx/sites-available/horsetrust /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
5

Set Up SSL with Let's Encrypt

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d horsetrust.com -d www.horsetrust.com -d api.horsetrust.com

Production Considerations

Security

Strong Secrets

Use cryptographically secure random strings for JWT_SECRET:
openssl rand -base64 32

CORS Restriction

Never use CORS_ORIGINS=* in production. Specify exact domains.

HTTPS Only

Always use SSL/TLS certificates. Use Let’s Encrypt for free certificates.

Rate Limiting

The server includes express-rate-limit - ensure it’s properly configured.

Database

Production Database Checklist:
  • Use MongoDB Atlas production tier with automated backups
  • Configure proper network security (IP whitelist)
  • Enable database auditing and monitoring
  • Set up connection pooling
  • Create appropriate indexes for queries
  • Implement backup and disaster recovery procedures

Performance

Server Optimization:
// Mongoose connection with production settings
mongoose.connect(uri, {
  serverSelectionTimeoutMS: 5000,
  socketTimeoutMS: 45000,
  maxPoolSize: 10,  // Add connection pooling
  minPoolSize: 5
});
Next.js Optimization:
// next.config.js
module.exports = {
  compress: true,
  poweredByHeader: false,
  generateEtags: true,
  images: {
    domains: ['res.cloudinary.com'],
    formats: ['image/avif', 'image/webp']
  }
};

Monitoring

1

Application Monitoring

Use PM2 monitoring or services like:
  • Datadog
  • New Relic
  • Sentry (error tracking)
2

Database Monitoring

MongoDB Atlas provides built-in monitoring:
  • Query performance
  • Connection metrics
  • Storage usage
3

Server Health Checks

Implement health check endpoints:
app.get('/health', (req, res) => {
  res.json({
    status: 'healthy',
    uptime: process.uptime(),
    timestamp: Date.now()
  });
});
4

Logging

The server uses Morgan for HTTP logging. In production:
// Use 'combined' format for detailed logs
app.use(morgan('combined'));

Graceful Shutdown

The server already implements graceful shutdown:
process.on('SIGTERM', () => {
  console.log('SIGTERM received. Shutting down...');
  httpServer.close(() => process.exit(0));
});
This ensures:
  • Active connections are completed
  • Socket.io connections are closed properly
  • Database connections are released

Continuous Deployment

GitHub Actions Example

# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '24'
      
      - name: Build Server
        run: |
          cd server
          npm ci
          npm run build
      
      - name: Build Client
        run: |
          cd client
          npm ci
          npm run build
      
      - name: Deploy
        # Add your deployment commands here
        run: echo "Deploy to your platform"

Troubleshooting Production Issues

  • Check for memory leaks with Node.js profiling
  • Ensure proper database connection pooling
  • Monitor Socket.io connections
  • Consider horizontal scaling
  • Add database indexes
  • Implement caching (Redis)
  • Optimize MongoDB queries
  • Use CDN for static assets
  • Enable gzip compression
  • Verify WebSocket support on your host
  • Check proxy configuration for WebSocket upgrades
  • Ensure sticky sessions for load balancers
  • Review CORS settings
  • Verify MongoDB Atlas IP whitelist
  • Check connection string format
  • Monitor connection pool usage
  • Review MongoDB Atlas metrics

Rollback Strategy

In case of deployment issues:
# Using PM2
pm2 list
pm2 stop all

# Revert to previous git commit
git checkout <previous-commit-hash>

# Rebuild and restart
npm install
npm run build
pm2 restart all

Next Steps

Monitoring Setup

Implement comprehensive monitoring and alerting

Backup Strategy

Set up automated database backups and recovery procedures

Load Testing

Perform load testing to identify bottlenecks

Documentation

Document your deployment process and runbooks

Build docs developers (and LLMs) love