Claude Code Plugins

Community-maintained marketplace

Feedback

Provides comprehensive Render.com deployment standards covering environment configuration, database migrations, cron jobs, health checks, log management, and production best practices for web services

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name deploy-render
description Provides comprehensive Render.com deployment standards covering environment configuration, database migrations, cron jobs, health checks, log management, and production best practices for web services

Render.com Deployment Standards

This skill provides complete guidelines for deploying applications to Render.com, covering all aspects from initial setup to production monitoring.

Pre-Deployment Checklist

Repository Requirements

  • Code pushed to GitHub/GitLab/Bitbucket
  • package.json with correct start script
  • Build command configured (if using build step)
  • .gitignore includes .env, node_modules, build artifacts
  • Dependencies properly listed (not in devDependencies if needed for production)
  • Database migrations ready (if applicable)
  • Health check endpoint implemented

Environment Preparation

  • Production environment variables documented
  • Secrets stored securely (not in repo)
  • Database connection strings prepared
  • Third-party API keys obtained
  • Domain/subdomain configured (if using custom domain)

Service Configuration

Web Service Setup

Basic Configuration:

# render.yaml (Infrastructure as Code - optional but recommended)
services:
  - type: web
    name: my-app
    env: node
    region: oregon
    plan: starter
    buildCommand: npm run build
    startCommand: npm start
    envVars:
      - key: NODE_ENV
        value: production
      - key: DATABASE_URL
        fromDatabase:
          name: my-postgres-db
          property: connectionString
      - key: API_KEY
        sync: false # Secret - set manually in dashboard
    healthCheckPath: /api/health
    autoDeploy: true

Key Settings:

Setting Recommended Value Notes
Environment Node, Docker, Python, etc. Based on your stack
Region oregon, frankfurt, singapore Choose closest to users
Plan starter → standard → pro Scale based on traffic
Build Command npm run build Empty if no build step
Start Command npm start Must be defined
Auto-Deploy true Deploy on git push
Health Check /api/health or /health Critical for zero-downtime

Environment Variables

Required Variables for Next.js:

# Core
NODE_ENV=production
PORT=10000  # Render assigns this automatically

# Application
NEXT_PUBLIC_SITE_URL=https://your-app.onrender.com
NEXTAUTH_URL=https://your-app.onrender.com
NEXTAUTH_SECRET=your-secret-key-min-32-chars

# Database (if using Render PostgreSQL)
DATABASE_URL=${DATABASE_URL}  # Auto-populated from database connection

# Supabase (example)
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key

# External APIs
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
SENDGRID_API_KEY=SG...

Setting Variables:

Via Dashboard:

  1. Go to your service → Environment
  2. Add environment variables
  3. Mark sensitive ones as "Secret"

Via render.yaml:

envVars:
  - key: NODE_ENV
    value: production
  - key: DATABASE_URL
    fromDatabase:
      name: my-postgres-db
      property: connectionString
  - key: API_SECRET
    generateValue: true  # Auto-generate random value
  - key: STRIPE_KEY
    sync: false  # Must set manually (secret)

Build & Start Commands

Next.js:

buildCommand: npm install && npm run build
startCommand: npm start

Vite/React:

buildCommand: npm install && npm run build
startCommand: npx serve -s dist -l $PORT

Node.js/Express:

buildCommand: npm install
startCommand: npm start

Docker:

dockerfilePath: ./Dockerfile
dockerCommand: npm start

TypeScript:

buildCommand: npm install && npm run build
startCommand: node dist/index.js

Database Configuration

PostgreSQL Setup

Create Database:

  1. Dashboard → New → PostgreSQL
  2. Select region (same as web service)
  3. Choose plan (Starter is free)
  4. Database created with auto-generated credentials

Connect to Web Service:

# render.yaml
databases:
  - name: my-postgres-db
    plan: starter
    region: oregon
    databaseName: myapp_db
    user: myapp_user

services:
  - type: web
    name: my-app
    envVars:
      - key: DATABASE_URL
        fromDatabase:
          name: my-postgres-db
          property: connectionString

Manual Connection String:

postgresql://user:password@host:port/database

Redis Setup (for caching/sessions)

# render.yaml
services:
  - type: redis
    name: my-redis
    plan: starter
    region: oregon
    maxmemoryPolicy: allkeys-lru

Environment Variable:

REDIS_URL=${REDIS_URL}  # Auto-populated

Database Migrations

Prisma Migration Strategy

Option 1: Run migrations in build command (Recommended)

buildCommand: npm install && npx prisma generate && npx prisma migrate deploy && npm run build

Option 2: Separate migration job

# render.yaml
services:
  - type: worker
    name: migration-runner
    env: node
    buildCommand: npm install && npx prisma generate
    startCommand: npx prisma migrate deploy && exit 0
    plan: starter
    envVars:
      - key: DATABASE_URL
        fromDatabase:
          name: my-postgres-db
          property: connectionString

Drizzle Migration

buildCommand: npm install && npm run db:migrate && npm run build

Migration script in package.json:

{
  "scripts": {
    "db:migrate": "drizzle-kit push:pg",
    "db:generate": "drizzle-kit generate:pg"
  }
}

Manual Migrations

For complex migrations, use a separate job:

# In your repo, create migrate.js
const { Pool } = require('pg');
const fs = require('fs');

async function runMigrations() {
  const pool = new Pool({ connectionString: process.env.DATABASE_URL });
  const sql = fs.readFileSync('./migrations/001_initial.sql', 'utf8');
  await pool.query(sql);
  await pool.end();
  console.log('Migrations complete');
}

runMigrations();
# render.yaml
jobs:
  - type: cron
    name: run-migrations
    schedule: "@manual"  # Run manually
    command: node migrate.js

Cron Jobs & Background Tasks

Cron Job Configuration

# render.yaml
services:
  - type: cron
    name: daily-cleanup
    schedule: "0 2 * * *"  # Every day at 2 AM UTC
    env: node
    buildCommand: npm install
    startCommand: node scripts/cleanup.js
    region: oregon
    envVars:
      - key: DATABASE_URL
        fromDatabase:
          name: my-postgres-db
          property: connectionString

Common Cron Schedules:

Schedule Expression Description
Every hour 0 * * * * At minute 0
Every 6 hours 0 */6 * * * At 00:00, 06:00, 12:00, 18:00
Daily at 2 AM 0 2 * * * Every day at 2:00 AM UTC
Weekly (Monday) 0 0 * * 1 Monday at midnight
Monthly (1st) 0 0 1 * * 1st of month at midnight

Cron Expression Format:

* * * * *
│ │ │ │ │
│ │ │ │ └─── Day of week (0-7, 0 and 7 = Sunday)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of month (1-31)
│ └───────── Hour (0-23)
└─────────── Minute (0-59)

Background Workers

# render.yaml
services:
  - type: worker
    name: email-worker
    env: node
    buildCommand: npm install
    startCommand: node workers/email-processor.js
    plan: starter
    envVars:
      - key: REDIS_URL
        fromService:
          type: redis
          name: my-redis
          property: connectionString

Example Worker (Bull Queue):

// workers/email-processor.js
import Queue from 'bull';

const emailQueue = new Queue('email', process.env.REDIS_URL);

emailQueue.process(async (job) => {
  const { to, subject, body } = job.data;
  // Send email logic
  console.log(`Sending email to ${to}`);
});

console.log('Email worker running...');

Health Checks

Implementation

Express.js:

// routes/health.ts
app.get('/api/health', async (req, res) => {
  try {
    // Check database connection
    await db.query('SELECT 1');
    
    // Check Redis (if applicable)
    await redis.ping();
    
    res.status(200).json({
      status: 'healthy',
      timestamp: new Date().toISOString(),
      uptime: process.uptime(),
      database: 'connected',
      redis: 'connected'
    });
  } catch (error) {
    res.status(503).json({
      status: 'unhealthy',
      error: error.message
    });
  }
});

Next.js API Route:

// app/api/health/route.ts
import { NextResponse } from 'next/server';

export async function GET() {
  try {
    // Basic health check
    return NextResponse.json({
      status: 'healthy',
      timestamp: new Date().toISOString()
    });
  } catch (error) {
    return NextResponse.json(
      { status: 'unhealthy', error: error.message },
      { status: 503 }
    );
  }
}

Configuration in Render:

healthCheckPath: /api/health

Or via Dashboard:

  • Service Settings → Health Check
  • Path: /api/health
  • Render will ping every 30 seconds
  • 3 failed checks = service marked unhealthy

Logging & Monitoring

Structured Logging

// utils/logger.ts
const logger = {
  info: (message: string, meta?: any) => {
    console.log(JSON.stringify({
      level: 'info',
      message,
      timestamp: new Date().toISOString(),
      ...meta
    }));
  },
  error: (message: string, error?: Error, meta?: any) => {
    console.error(JSON.stringify({
      level: 'error',
      message,
      error: error?.message,
      stack: error?.stack,
      timestamp: new Date().toISOString(),
      ...meta
    }));
  },
  warn: (message: string, meta?: any) => {
    console.warn(JSON.stringify({
      level: 'warn',
      message,
      timestamp: new Date().toISOString(),
      ...meta
    }));
  }
};

export default logger;

Usage:

logger.info('User logged in', { userId: user.id });
logger.error('Database connection failed', error);

Viewing Logs

Via Dashboard:

  1. Go to your service
  2. Click "Logs" tab
  3. View real-time logs
  4. Filter by date/time

Via CLI:

# Install Render CLI
npm install -g @render/cli

# Login
render login

# View logs
render logs my-app --tail
render logs my-app --since 1h

Log Aggregation (Advanced)

Integration with LogDNA/Datadog:

// Log forwarding
const logForwarder = require('logdna-winston');

const logger = winston.createLogger({
  transports: [
    new logForwarder({
      key: process.env.LOGDNA_KEY,
      app: 'my-app',
      env: process.env.NODE_ENV
    })
  ]
});

Custom Domains

Setup Steps

  1. Add domain in Render:

    • Service Settings → Custom Domain
    • Enter your domain (e.g., app.yourdomain.com)
  2. Configure DNS:

    • Add CNAME record pointing to Render
    Type: CNAME
    Name: app (or www)
    Value: your-app.onrender.com
    TTL: 3600
    
  3. SSL Certificate:

    • Automatically provisioned by Render (Let's Encrypt)
    • Takes 5-10 minutes after DNS propagation

Apex Domain (yourdomain.com)

Type: ALIAS or ANAME (if provider supports)
Name: @
Value: your-app.onrender.com

Or use A records (provided by Render in dashboard)

Force HTTPS

// middleware.ts (Next.js)
export function middleware(request: NextRequest) {
  const proto = request.headers.get('x-forwarded-proto');
  
  if (proto !== 'https') {
    return NextResponse.redirect(
      `https://${request.headers.get('host')}${request.nextUrl.pathname}`,
      301
    );
  }
}

Scaling Configuration

Horizontal Scaling

# render.yaml
services:
  - type: web
    name: my-app
    scaling:
      minInstances: 1
      maxInstances: 10
      targetMemoryPercent: 80
      targetCPUPercent: 70

Manual Scaling (via Dashboard):

  • Service Settings → Scaling
  • Adjust instance count
  • Immediate effect

Vertical Scaling

Upgrade Plan:

  • Starter (512 MB RAM, 0.5 CPU)
  • Standard (2 GB RAM, 1 CPU)
  • Pro (4 GB RAM, 2 CPU)
  • Pro Plus (8 GB RAM, 4 CPU)

Zero-Downtime Deployments

Strategy

Render performs zero-downtime deployments automatically:

  1. New version built
  2. Health check passes on new instance
  3. Traffic gradually shifted to new instance
  4. Old instance terminated after drain period

Ensure zero-downtime:

  • Health check endpoint returns 200
  • Database migrations are backwards-compatible
  • No breaking API changes

Rollback

Via Dashboard:

  1. Service → Deploys
  2. Find previous successful deploy
  3. Click "Rollback to this version"

Via render.yaml:

# Revert git commit and push
git revert HEAD
git push origin main

Deployment Triggers

Auto-Deploy

Enable:

services:
  - type: web
    name: my-app
    autoDeploy: true  # Deploy on every push to main
    branch: main

Manual Deploy:

autoDeploy: false

Deploy manually via:

  • Dashboard → "Manual Deploy"
  • CLI: render deploy my-app

Deploy Hooks

Webhook URL:

  • Service Settings → Deploy Hook
  • Copy webhook URL
  • Trigger deploys via HTTP POST
curl -X POST https://api.render.com/deploy/srv-xxx?key=xxx

Use cases:

  • CI/CD pipelines
  • Automated workflows
  • External monitoring systems

Production Best Practices

Security

  • Use environment secrets for sensitive data
  • Enable HTTPS (automatic with Render)
  • Set secure headers (helmet.js for Node)
  • Implement rate limiting
  • Use CORS properly
  • Keep dependencies updated
  • Run security audits (npm audit)

Performance

  • Enable compression (gzip/brotli)
  • Implement caching (Redis, CDN)
  • Optimize database queries (indexes, connection pooling)
  • Use CDN for static assets
  • Monitor response times
  • Set appropriate timeouts

Reliability

  • Implement health checks
  • Set up error tracking (Sentry, Rollbar)
  • Configure alerting
  • Test deployments in staging first
  • Document rollback procedures
  • Monitor resource usage (CPU, memory)

Cost Optimization

  • Use starter plan for low-traffic services
  • Scale down non-production environments
  • Use suspending services for dev/staging
  • Optimize build times (caching)
  • Monitor bandwidth usage

Troubleshooting

Common Issues

Build Fails:

# Check logs for error
# Common causes:
- Missing dependencies in package.json
- Build command incorrect
- Out of memory (upgrade plan)

Service Won't Start:

# Verify:
- Start command is correct
- Port binding: app.listen(process.env.PORT || 10000)
- Environment variables set correctly

Database Connection Fails:

# Check:
- DATABASE_URL is set
- Database is in same region
- IP allowlist (not needed on Render)
- Connection pool limits

Health Check Fails:

# Verify:
- /api/health endpoint exists
- Returns 200 status
- Responds within 10 seconds
- No dependencies fail (DB, Redis)

Monitoring Checklist

  • Health check endpoint responding
  • Logs streaming properly
  • Deployment notifications configured
  • Error tracking integrated (Sentry, etc.)
  • Database performance monitored
  • Uptime monitoring (UptimeRobot, Pingdom)
  • SSL certificate valid
  • Custom domain resolving correctly
  • Backup strategy in place (database)
  • Disaster recovery plan documented

Resources


Critical Reminder: Always test deployments in a staging environment before pushing to production. Keep deployment scripts and documentation updated as your application evolves.