ShopySeed

Deployment Guide

Deploy ShopySeed to production with Docker, Vercel, AWS ECS, Kubernetes, and other cloud platforms.

This guide covers deploying ShopySeed to production environments, from simple Docker deployments to scalable cloud infrastructure.

Table of Contents

Prerequisites

Production Requirements

ComponentMinimumRecommendedNotes
CPU1 vCPU2+ vCPUsFor API server
RAM1GB4GB+Node.js + PostgreSQL + Redis
Storage10GB50GB+Database, logs, uploads
Bandwidth100Mbps1GbpsFor user uploads/downloads

Required Services

  • PostgreSQL 16+: Primary database
  • Redis 7+: Caching and session storage
  • SMTP Service: Email delivery (Resend, SendGrid, etc.)
  • Stripe Account: Payment processing
  • Domain & SSL: HTTPS certificate

Environment Configuration

Production Environment Files

Create production environment configuration:

# Copy production templates
cp .env.production.example .env.production

# Configure API environment
cp apps/api/.env.production.example apps/api/.env.production

# Configure Web environment  
cp apps/web/.env.production.example apps/web/.env.production

Core Environment Variables

API Configuration (apps/api/.env.production)

# === APPLICATION ===
NODE_ENV=production
PORT=3011
LOG_LEVEL=info

# === DATABASE ===
DATABASE_URL=postgresql://postgres:STRONG_PASSWORD@postgres:5432/shopyseed_prod?schema=public
POSTGRES_USER=postgres
POSTGRES_PASSWORD=STRONG_PASSWORD_HERE
POSTGRES_DB=shopyseed_prod
POSTGRES_PORT=5433

# === REDIS ===
REDIS_URL=redis://redis:6379
REDIS_PORT=6379

# === SECURITY ===
JWT_SECRET=GENERATE_STRONG_32_CHAR_SECRET_HERE
CORS_ORIGINS=https://yourdomain.com,https://www.yourdomain.com

# === RATE LIMITING ===
RATE_LIMIT_TTL=60000
RATE_LIMIT_MAX=100

# === STRIPE ===
STRIPE_SECRET_KEY=sk_live_YOUR_LIVE_STRIPE_KEY
STRIPE_WEBHOOK_SECRET=whsec_YOUR_WEBHOOK_SECRET

# === EMAIL ===
EMAIL_HOST=smtp.sendgrid.net
EMAIL_PORT=587
EMAIL_USER=apikey
EMAIL_PASSWORD=YOUR_SENDGRID_API_KEY
EMAIL_FROM=noreply@yourdomain.com

# === OAUTH (Optional) ===
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret

Web Configuration (apps/web/.env.production)

# === API CONNECTION ===
NEXT_PUBLIC_API_URL=https://api.yourdomain.com

# === STRIPE ===
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_live_YOUR_LIVE_PUBLISHABLE_KEY

# === OAUTH URLS ===
NEXT_PUBLIC_GOOGLE_OAUTH_URL=https://api.yourdomain.com/auth/oauth/google
NEXT_PUBLIC_GITHUB_OAUTH_URL=https://api.yourdomain.com/auth/oauth/github

# === ANALYTICS (Optional) ===
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX

Security Best Practices

Generate Secure Secrets

# JWT Secret (minimum 32 characters)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

# Strong PostgreSQL password
openssl rand -base64 32

# Stripe webhook secret
# Get from Stripe Dashboard → Webhooks → Endpoint → Signing secret

Environment Security Checklist

  • All secrets are generated uniquely for production
  • No development credentials are used
  • Environment files are never committed to git
  • Secrets are stored in secure secret management (AWS Secrets, etc.)
  • CORS_ORIGINS only includes your production domains
  • Rate limiting is appropriately configured for production load

Docker Production Deployment

Single-Server Docker Deployment

The simplest production deployment uses Docker Compose on a single server.

1. Prepare Server

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

# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Install Docker Compose
sudo apt install docker-compose-plugin -y

# Create application user
sudo useradd -m -s /bin/bash shopyseed
sudo usermod -aG docker shopyseed

2. Deploy Application

# Clone repository
git clone https://github.com/yourusername/shopyseed.git /home/shopyseed/app
cd /home/shopyseed/app

# Setup environment
cp .env.production.example .env.production
cp apps/api/.env.production.example apps/api/.env.production
cp apps/web/.env.production.example apps/web/.env.production

# Edit environment files with production values
nano apps/api/.env.production
nano apps/web/.env.production

# Build and start services
docker compose -f docker-compose.prod.yml up -d --build

# Check services are running
docker compose -f docker-compose.prod.yml ps

3. Database Migration

# Run database migrations
docker compose -f docker-compose.prod.yml exec api pnpm prisma migrate deploy

# (Optional) Seed production data
docker compose -f docker-compose.prod.yml exec api pnpm db:seed:prod

Docker Services

The production Docker Compose includes:

# docker-compose.prod.yml (simplified)
services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER} 
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    
  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
      
  api:
    build:
      context: .
      dockerfile: apps/api/Dockerfile.prod
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_URL=${REDIS_URL}
      - JWT_SECRET=${JWT_SECRET}
    depends_on:
      - postgres
      - redis
      
  web:
    build:
      context: .
      dockerfile: apps/web/Dockerfile.prod
    environment:
      - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
    depends_on:
      - api
      
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - certbot_certs:/etc/letsencrypt
    depends_on:
      - api
      - web

Nginx Configuration

# nginx.conf
upstream api {
    server api:3011;
}

upstream web {
    server web:3000;
}

server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com api.yourdomain.com;
    
    # Redirect to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;
    
    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    
    # Security headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
    
    location / {
        proxy_pass http://web;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 443 ssl http2;
    server_name api.yourdomain.com;
    
    ssl_certificate /etc/letsencrypt/live/api.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.yourdomain.com/privkey.pem;
    
    location / {
        proxy_pass http://api;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # API-specific settings
        proxy_read_timeout 60s;
        proxy_connect_timeout 10s;
        proxy_send_timeout 60s;
        
        # Handle large uploads
        client_max_body_size 100M;
    }
}

Cloud Platform Deployments

Vercel + PlanetScale + Redis Cloud

A popular serverless deployment option:

1. Deploy Web App to Vercel

# Install Vercel CLI
npm i -g vercel

# Deploy from apps/web directory
cd apps/web
vercel

# Configure environment variables in Vercel dashboard
# - NEXT_PUBLIC_API_URL
# - NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY

2. Deploy API to Railway/Render

# Create Dockerfile for API
# apps/api/Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3011
CMD ["npm", "run", "start:prod"]

3. Setup PlanetScale Database

# Install PlanetScale CLI
curl -fsSL https://get.planetscale.com/install.sh | bash

# Create database
pscale database create shopyseed-prod

# Get connection string
pscale connect shopyseed-prod main --port 3309

# Update DATABASE_URL in deployment environment

AWS ECS Deployment

For more control and scalability:

1. ECS Task Definition

{
  "family": "shopyseed-api",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "executionRoleArn": "arn:aws:iam::account:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "name": "api",
      "image": "your-ecr-repo/shopyseed-api:latest",
      "portMappings": [
        {
          "containerPort": 3011,
          "protocol": "tcp"
        }
      ],
      "environment": [
        {
          "name": "NODE_ENV",
          "value": "production"
        }
      ],
      "secrets": [
        {
          "name": "DATABASE_URL",
          "valueFrom": "arn:aws:secretsmanager:region:account:secret:shopyseed/database-url"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/shopyseed-api",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}

Kubernetes Deployment

For large-scale deployments:

# k8s/api-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: shopyseed-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: shopyseed-api
  template:
    metadata:
      labels:
        app: shopyseed-api
    spec:
      containers:
      - name: api
        image: your-registry/shopyseed-api:latest
        ports:
        - containerPort: 3011
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: shopyseed-secrets
              key: database-url
        - name: REDIS_URL
          valueFrom:
            secretKeyRef:
              name: shopyseed-secrets
              key: redis-url
        resources:
          requests:
            memory: "512Mi"
            cpu: "250m"
          limits:
            memory: "1Gi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3011
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /health/ready
            port: 3011
          initialDelaySeconds: 5
          periodSeconds: 5

Database Setup

PostgreSQL Production Configuration

# postgresql.conf optimizations
shared_buffers = 256MB              # 25% of RAM
effective_cache_size = 1GB          # 75% of RAM  
work_mem = 4MB                      # Per operation
maintenance_work_mem = 64MB         # Maintenance operations
max_connections = 100               # Adjust based on load

Database Security

-- Create application user with limited permissions
CREATE USER shopyseed_app WITH PASSWORD 'strong_password';

-- Grant necessary permissions only
GRANT CONNECT ON DATABASE shopyseed_prod TO shopyseed_app;
GRANT USAGE ON SCHEMA public TO shopyseed_app;
GRANT CREATE ON SCHEMA public TO shopyseed_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO shopyseed_app;

Backup Strategy

#!/bin/bash
# backup.sh - Daily database backup script

DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/backups"
DB_NAME="shopyseed_prod"

# Create backup
pg_dump -h localhost -U postgres -d $DB_NAME -f "$BACKUP_DIR/shopyseed_${DATE}.sql"

# Compress backup
gzip "$BACKUP_DIR/shopyseed_${DATE}.sql"

# Keep only last 7 days
find $BACKUP_DIR -name "shopyseed_*.sql.gz" -mtime +7 -delete

# Upload to S3 (optional)
aws s3 cp "$BACKUP_DIR/shopyseed_${DATE}.sql.gz" s3://your-backup-bucket/database/

Stripe Configuration

Production Stripe Setup

1. Activate Live Mode

  1. Complete Stripe account verification
  2. Switch to Live mode in Stripe Dashboard
  3. Get live API keys

2. Configure Webhooks

# Webhook endpoint URL
https://api.yourdomain.com/billing/webhooks

# Events to listen for:
- customer.subscription.created
- customer.subscription.updated  
- customer.subscription.deleted
- invoice.payment_succeeded
- invoice.payment_failed

3. Payment Methods Configuration

// Enable payment methods in Stripe Dashboard
const paymentMethods = [
  'card',           // Credit/debit cards
  'sepa_debit',     // European bank transfers
  'ideal',          // Netherlands
  'giropay',        // Germany
  'p24',            // Poland
  'bancontact',     // Belgium
];

4. Tax Configuration

// Configure tax rates in Stripe
const taxRates = {
  'US': 0.0875,     // 8.75% sales tax
  'EU': 0.20,       // 20% VAT
  'CA': 0.13,       // 13% HST
};

Email Configuration

ProviderCostFeaturesSetup Difficulty
ResendFree tier availableDeveloper-friendly, great deliverabilityEasy
SendGridFree tier availableRobust feature set, analyticsMedium
Amazon SESVery low costHigh volume, requires setupHard
MailgunFree tier availableGood for transactional emailsEasy
# Environment variables
EMAIL_HOST=smtp.resend.com
EMAIL_PORT=587
EMAIL_USER=resend
EMAIL_PASSWORD=your-resend-api-key
EMAIL_FROM=noreply@yourdomain.com

Email Templates

Configure email templates for:

  • Welcome email after registration
  • Email verification
  • Password reset
  • Organization invitations
  • Billing notifications
// Email template configuration
const emailTemplates = {
  welcome: {
    subject: 'Welcome to ShopySeed!',
    template: 'welcome.html'
  },
  verification: {
    subject: 'Verify your email address',
    template: 'verification.html'
  },
  invitation: {
    subject: 'You\'ve been invited to join {{organizationName}}',
    template: 'invitation.html'
  }
};

Security & SSL

SSL Certificate Setup

Let's Encrypt with Certbot

# Install Certbot
sudo apt install certbot python3-certbot-nginx

# Get certificates
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com -d api.yourdomain.com

# Auto-renewal
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

Manual Certificate Installation

# nginx SSL configuration
ssl_certificate /path/to/certificate.pem;
ssl_certificate_key /path/to/private.key;

# SSL security settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

Security Headers

# Security headers in Nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://js.stripe.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.stripe.com;" always;

Firewall Configuration

# UFW firewall setup
sudo ufw enable
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Database access (only from application server)
sudo ufw allow from 10.0.0.0/24 to any port 5432

Monitoring & Health Checks

Application Monitoring

Health Check Endpoints

ShopySeed provides built-in health check endpoints:

# Basic health check
curl https://api.yourdomain.com/health

# Readiness check (includes database)
curl https://api.yourdomain.com/health/ready

Response Examples

// Healthy response
{
  "status": "ok",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "uptime": 3600,
  "version": "1.0.0",
  "checks": {
    "database": { "status": "ok", "responseTime": 12 },
    "redis": { "status": "ok", "responseTime": 3 }
  }
}

// Unhealthy response (503 status)
{
  "status": "error", 
  "timestamp": "2024-01-15T10:30:00.000Z",
  "checks": {
    "database": { "status": "error", "error": "Connection timeout" },
    "redis": { "status": "ok", "responseTime": 3 }
  }
}

External Monitoring

Uptime Monitoring

Configure external monitors to check:

  • Main application URL
  • API health endpoints
  • Database connectivity
  • Payment processing

Recommended Services:

  • UptimeRobot (free tier available)
  • Pingdom
  • New Relic
  • DataDog

Log Aggregation

# docker-compose.prod.yml - Add logging
services:
  api:
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "5"
        labels: "service=api"

Alerting

Set up alerts for:

  • Application downtime
  • Database connection failures
  • High error rates (>5%)
  • Payment processing failures
  • Disk space usage (>80%)
  • Memory usage (>90%)

Backup & Recovery

Database Backups

Automated Backup Script

#!/bin/bash
# /usr/local/bin/backup-shopyseed.sh

set -e

# Configuration
BACKUP_DIR="/backups/shopyseed"
DB_HOST="localhost"
DB_NAME="shopyseed_prod"
DB_USER="postgres"
S3_BUCKET="your-backup-bucket"
RETENTION_DAYS=30

# Create backup directory
mkdir -p $BACKUP_DIR

# Create backup
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/shopyseed_${DATE}.sql"

echo "Creating backup: $BACKUP_FILE"
pg_dump -h $DB_HOST -U $DB_USER -d $DB_NAME > $BACKUP_FILE

# Compress
gzip $BACKUP_FILE
BACKUP_FILE_GZ="${BACKUP_FILE}.gz"

# Upload to S3
aws s3 cp $BACKUP_FILE_GZ s3://$S3_BUCKET/database/

# Clean old local backups
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete

# Clean old S3 backups
aws s3api list-objects-v2 --bucket $S3_BUCKET --prefix database/ --query 'Contents[?LastModified<=`'$(date -d "$RETENTION_DAYS days ago" --iso-8601)'`].Key' --output text | xargs -I {} aws s3 rm s3://$S3_BUCKET/{}

echo "Backup completed successfully"

Cron Job Setup

# Add to crontab: crontab -e
# Daily backup at 2 AM
0 2 * * * /usr/local/bin/backup-shopyseed.sh >> /var/log/shopyseed-backup.log 2>&1

# Weekly backup verification
0 3 * * 0 /usr/local/bin/verify-backup.sh >> /var/log/shopyseed-backup-verify.log 2>&1

Disaster Recovery Plan

Recovery Time Objectives (RTO)

  • Database Recovery: < 1 hour
  • Application Recovery: < 30 minutes
  • Complete System Recovery: < 4 hours

Recovery Procedures

# 1. Restore database from backup
pg_restore -h localhost -U postgres -d shopyseed_prod_restored backup_file.sql.gz

# 2. Update application configuration
# Point DATABASE_URL to restored database

# 3. Restart application services
docker compose -f docker-compose.prod.yml restart

# 4. Verify application functionality
curl https://api.yourdomain.com/health/ready

Performance Optimization

Database Performance

Index Optimization

-- Create indexes for common queries
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
CREATE INDEX CONCURRENTLY idx_organization_members_org_id ON organization_members(organization_id);
CREATE INDEX CONCURRENTLY idx_subscriptions_org_id ON subscriptions(organization_id);

-- Multi-tenant queries
CREATE INDEX CONCURRENTLY idx_tenant_data_org_id ON tenant_data(organization_id, created_at);

Query Optimization

// Use Prisma query optimization
const organizations = await prisma.organization.findMany({
  where: { id: { in: userOrganizationIds } },
  include: {
    members: {
      select: { id: true, role: true, user: { select: { id: true, email: true } } }
    },
    subscription: { select: { plan: true, status: true } }
  }
});

Application Performance

Caching Strategy

// Redis caching implementation
@Injectable()
export class CacheService {
  constructor(private redis: Redis) {}

  async getOrSet<T>(key: string, fetcher: () => Promise<T>, ttl = 3600): Promise<T> {
    const cached = await this.redis.get(key);
    if (cached) {
      return JSON.parse(cached);
    }

    const data = await fetcher();
    await this.redis.setex(key, ttl, JSON.stringify(data));
    return data;
  }
}

Connection Pooling

// Prisma connection pooling
const prisma = new PrismaClient({
  datasources: {
    db: {
      url: process.env.DATABASE_URL
    }
  },
  // Connection pooling
  log: ['warn', 'error'],
});

Frontend Performance

Next.js Optimization

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  // Enable compression
  compress: true,
  
  // Image optimization
  images: {
    domains: ['yourdomain.com'],
    formats: ['image/webp', 'image/avif'],
  },
  
  // Bundle analyzer
  webpack: (config, { dev, isServer }) => {
    if (!dev && !isServer) {
      config.optimization.splitChunks.cacheGroups = {
        ...config.optimization.splitChunks.cacheGroups,
        commons: {
          name: 'commons',
          chunks: 'initial',
          minChunks: 2
        }
      };
    }
    return config;
  }
};

module.exports = nextConfig;

Scaling Considerations

Horizontal Scaling

Load Balancing

# nginx load balancer configuration
upstream api_backend {
    least_conn;
    server api-1:3011;
    server api-2:3011;
    server api-3:3011;
    
    # Health checks
    health_check interval=30s fails=3 passes=2;
}

Database Scaling

// Read replica configuration
const prismaRead = new PrismaClient({
  datasources: {
    db: { url: process.env.DATABASE_READ_URL }
  }
});

const prismaWrite = new PrismaClient({
  datasources: {
    db: { url: process.env.DATABASE_WRITE_URL }
  }
});

// Use read replica for queries
const organizations = await prismaRead.organization.findMany();

// Use primary for writes
await prismaWrite.organization.create(data);

Auto-Scaling

Kubernetes HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: shopyseed-api-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: shopyseed-api
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

Troubleshooting

Common Issues

1. Database Connection Failures

# Check database connectivity
docker compose -f docker-compose.prod.yml exec postgres psql -U postgres -d shopyseed_prod -c "SELECT version();"

# Check connection pool exhaustion
docker compose -f docker-compose.prod.yml logs api | grep "connection"

2. Memory Issues

# Check memory usage
docker stats

# Check Node.js memory usage
docker compose -f docker-compose.prod.yml exec api node -e "console.log(process.memoryUsage())"

3. SSL Certificate Issues

# Check certificate expiration
openssl x509 -in /etc/letsencrypt/live/yourdomain.com/cert.pem -text -noout | grep "Not After"

# Test SSL configuration
curl -I https://yourdomain.com

Debugging Steps

  1. Check Service Status

    docker compose -f docker-compose.prod.yml ps
  2. Review Logs

    docker compose -f docker-compose.prod.yml logs --tail=100 api
  3. Test Connectivity

    curl -v https://api.yourdomain.com/health
  4. Database Health

    docker compose -f docker-compose.prod.yml exec postgres pg_isready
  5. Resource Usage

    docker system df
    docker system events

Production Checklist

Before going live, ensure you have completed:

Security

  • SSL certificates installed and configured
  • All default passwords changed
  • Firewall properly configured
  • Security headers implemented
  • Rate limiting enabled
  • CORS configured for production domains only

Performance

  • Database optimized and indexed
  • Caching implemented
  • CDN configured for static assets
  • Images optimized
  • Bundle sizes minimized

Monitoring

  • Health checks implemented
  • Uptime monitoring configured
  • Log aggregation set up
  • Error tracking implemented
  • Performance monitoring active

Backup & Recovery

  • Automated database backups
  • Backup restoration tested
  • Disaster recovery plan documented
  • Recovery procedures tested

Documentation

  • API documentation updated
  • Deployment procedures documented
  • Troubleshooting guides created
  • Contact information for emergencies

Your ShopySeed application is now ready for production! 🚀

On this page

Table of ContentsPrerequisitesProduction RequirementsRequired ServicesEnvironment ConfigurationProduction Environment FilesCore Environment VariablesAPI Configuration (apps/api/.env.production)Web Configuration (apps/web/.env.production)Security Best PracticesGenerate Secure SecretsEnvironment Security ChecklistDocker Production DeploymentSingle-Server Docker Deployment1. Prepare Server2. Deploy Application3. Database MigrationDocker ServicesNginx ConfigurationCloud Platform DeploymentsVercel + PlanetScale + Redis Cloud1. Deploy Web App to Vercel2. Deploy API to Railway/Render3. Setup PlanetScale DatabaseAWS ECS Deployment1. ECS Task DefinitionKubernetes DeploymentDatabase SetupPostgreSQL Production ConfigurationRecommended SettingsDatabase SecurityBackup StrategyStripe ConfigurationProduction Stripe Setup1. Activate Live Mode2. Configure Webhooks3. Payment Methods Configuration4. Tax ConfigurationEmail ConfigurationRecommended Email ProvidersResend Configuration (Recommended)Email TemplatesSecurity & SSLSSL Certificate SetupLet's Encrypt with CertbotManual Certificate InstallationSecurity HeadersFirewall ConfigurationMonitoring & Health ChecksApplication MonitoringHealth Check EndpointsResponse ExamplesExternal MonitoringUptime MonitoringLog AggregationAlertingBackup & RecoveryDatabase BackupsAutomated Backup ScriptCron Job SetupDisaster Recovery PlanRecovery Time Objectives (RTO)Recovery ProceduresPerformance OptimizationDatabase PerformanceIndex OptimizationQuery OptimizationApplication PerformanceCaching StrategyConnection PoolingFrontend PerformanceNext.js OptimizationScaling ConsiderationsHorizontal ScalingLoad BalancingDatabase ScalingAuto-ScalingKubernetes HPATroubleshootingCommon Issues1. Database Connection Failures2. Memory Issues3. SSL Certificate IssuesDebugging StepsProduction ChecklistSecurityPerformanceMonitoringBackup & RecoveryDocumentation