Deploying to Production: Vercel vs Cloudflare vs Self-Hosted
Back to Blog

Deploying to Production: Vercel vs Cloudflare vs Self-Hosted

March 23, 20263 min read4 views

Where you deploy affects performance, cost, and complexity. Vercel offers polish, Cloudflare brings edge power, and self-hosting gives control. Here's how to choose.

Vercel: The Next.js Native Option

Pros:

  • Zero-config Next.js deployment
  • Excellent preview deployments
  • Built-in analytics and observability
  • Team collaboration features

Cons:

  • Premium pricing at scale
  • Function duration limits
  • Vendor lock-in concerns
// vercel.json
{
  "functions": {
    "app/api/**": {
      "maxDuration": 60  // Pro plan required for >10s
    }
  },
  "regions": ["iad1", "sfo1", "cdg1"]  // Deploy to multiple regions
}

Cloudflare Pages: Edge-First

Pros:

  • Generous free tier
  • True edge deployment globally
  • Integrated with Workers, KV, D1
  • Excellent performance

Cons:

  • Next.js support via adapter (not native)
  • Some features require adaptation
  • Smaller ecosystem
// next.config.js for Cloudflare
module.exports = {
  experimental: {
    runtime: 'edge',
  },
}

// Deploy
// npx @cloudflare/next-on-pages
// wrangler pages deploy .vercel/output/static

Self-Hosting: Docker and Kubernetes

Pros:

  • Complete control
  • No vendor lock-in
  • Can be cheapest at scale
  • Custom infrastructure possible

Cons:

  • Operational overhead
  • You handle scaling, security, uptime
  • Slower iteration
# Dockerfile for Next.js
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public

EXPOSE 3000
CMD ["node", "server.js"]

Cost Comparison at Scale

const monthlyEstimates = {
  // 1M page views, 100K function invocations
  
  vercel: {
    pro: 20,
    bandwidth: 0,      // Included
    functions: 0,      // Included up to limit
    analytics: 0,      // Basic included
    total: 20,
    // At higher scale: $150-500+
  },
  
  cloudflare: {
    pages: 0,          // Free tier
    workers: 5,        // Paid Workers
    bandwidth: 0,      // Free
    total: 5,
    // At higher scale: $25-100
  },
  
  selfHosted: {
    server: 50,        // VPS or cloud VM
    bandwidth: 20,     // Varies by provider
    monitoring: 20,    // Datadog, etc.
    time: 'priceless', // Your time
    total: 90,
    // At higher scale: $200-2000 but more predictable
  },
}

Migration Strategies

// Vercel → Self-hosted
// 1. Ensure standalone output
module.exports = {
  output: 'standalone',
}

// 2. Build Docker image
// 3. Set up infrastructure (K8s, Docker Compose)
// 4. Configure CDN for static assets
// 5. Set up monitoring
// 6. Gradual traffic migration

// Self-hosted → Vercel
// 1. Remove custom server code
// 2. Migrate env vars to Vercel
// 3. Ensure API routes fit function limits
// 4. Set up Vercel project
// 5. Connect domain
// 6. Done (much simpler!)

Decision Matrix

function recommendPlatform(requirements) {
  if (requirements.team.size < 5 && requirements.budget === 'startup') {
    return 'Cloudflare Pages'  // Best value
  }
  
  if (requirements.nextjs && requirements.prioritize === 'DX') {
    return 'Vercel'  // Best experience
  }
  
  if (requirements.scale === 'large' && requirements.team.hasDevOps) {
    return 'Self-hosted'  // Best economics at scale
  }
  
  if (requirements.compliance === 'strict') {
    return 'Self-hosted'  // Best control
  }
  
  return 'Vercel'  // Safe default
}

Key Takeaways

Vercel: Best DX, premium pricing, ideal for teams prioritizing velocity.

Cloudflare: Best value, edge-first, requires some adaptation.

Self-hosted: Best control, most work, cheapest at very large scale.

Share this article