Back to Blog

Migrating from Vercel to Cloudflare Workers: A Complete Platform Switch

How I migrated my Next.js site from Vercel + Supabase to Cloudflare Workers + D1 Database, cutting costs by 75% while eliminating database downtime.

·6 min read·Chris Knuteson
cloudflarevercelmigrationd1-databaseworkerscost-optimization

I recently completed a full platform migration for CTK Advisors, moving from Vercel Pro and Supabase to Cloudflare Workers with D1 Database. The migration reduced monthly costs from $20 to $5 while eliminating the database downtime issues I was experiencing with Supabase's free tier.

The Problem

My setup was costing $20/month for Vercel Pro, plus dealing with Supabase's free tier limitations. The main pain point was database pausing - my contact form would randomly fail when the database went to sleep, creating a poor user experience. I needed a solution that was both more reliable and cost-effective.

The Solution: Cloudflare Workers + D1

Cloudflare's edge platform offered everything I needed:

  • Workers: Serverless compute that runs globally
  • D1 Database: SQLite-based database that doesn't pause
  • KV Storage: Key-value store for logging and fallback
  • Custom domains: Included without extra cost

The total cost? $5/month for the Workers Paid plan, with generous included quotas that cover my usage completely.

Technical Implementation

Database Migration

The biggest challenge was migrating from PostgreSQL (Supabase) to SQLite (D1). I created a new schema optimized for D1:

CREATE TABLE IF NOT EXISTS contact_submissions (
    id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
    name TEXT NOT NULL,
    email TEXT NOT NULL,
    subject TEXT NOT NULL,
    project_type TEXT NOT NULL,
    budget_range TEXT,
    message TEXT NOT NULL,
    status TEXT NOT NULL DEFAULT 'new',
    submitted_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

Fallback Strategy

One of the key improvements was implementing a robust fallback system:

  1. Primary: D1 Database for contact form submissions
  2. Secondary: KV Storage if D1 fails
  3. Tertiary: Email notification as last resort

This ensures the contact form never completely fails, even if there are infrastructure issues.

Environment Access

The trickiest part was accessing Cloudflare's environment in Next.js server actions. The solution was using the context symbol:

function getCloudflareEnv(): CloudflareEnv {
  const context = (globalThis as any)[Symbol.for("__cloudflare-context__")];

  if (context && context.env) {
    return context.env;
  }

  throw new Error("Cloudflare environment not available");
}

Logging System

I replaced external monitoring with a KV-based logging system that automatically rotates logs and provides structured error tracking:

export class CloudflareLogger {
  constructor(private logsKV: KVNamespace) {}

  async log(
    level: "info" | "warn" | "error",
    message: string,
    context?: Record<string, any>
  ) {
    const entry = {
      id: crypto.randomUUID(),
      level,
      message,
      timestamp: new Date().toISOString(),
      context,
    };

    const logKey = `log_${Date.now()}_${entry.id}`;
    await this.logsKV.put(logKey, JSON.stringify(entry), {
      expirationTtl: 30 * 24 * 60 * 60, // 30 days
    });
  }
}

Deployment Process

The migration involved several steps:

  1. Infrastructure Setup: Created D1 database and KV namespaces using Wrangler CLI
  2. Code Migration: Updated database connections and server actions
  3. Domain Transfer: Removed Vercel DNS records and configured Cloudflare Workers custom domains
  4. CI/CD Setup: Configured GitHub Actions for automatic deployment

The domain switch was seamless - I simply removed the existing DNS records pointing to Vercel and deployed the Worker with custom domain configuration.

Performance and Reliability

The results have been impressive:

  • Global Edge Deployment: The site now runs from Cloudflare's edge locations worldwide
  • Zero Database Downtime: D1 doesn't pause like Supabase's free tier
  • Faster Response Times: Edge computing reduces latency significantly
  • Built-in CDN: Static assets are automatically cached globally

Cost Breakdown

Before (Monthly):

  • Vercel Pro: $20
  • Supabase Free: $0 (with downtime issues)
  • Total: $20/month

After (Monthly):

  • Cloudflare Workers Paid: $5
  • D1 Database: $0 (within included limits)
  • KV Storage: $0 (within included limits)
  • Total: $5/month

Annual Savings: $180

Using Kiro's Agentic Development Flow

I built this migration using Kiro's structured approach: design → requirements → tasks → implement. Instead of jumping straight into code, Kiro forces you through a methodical process that catches issues early.

The Process

Design Phase: I described the migration goal - move from Vercel + Supabase to Cloudflare Workers + D1, reduce costs, eliminate database downtime. Kiro analyzed the current architecture and proposed the new structure.

Requirements Gathering: Kiro broke down what needed to happen:

  • Database schema conversion (PostgreSQL → SQLite)
  • Environment variable handling for Cloudflare context
  • Fallback strategy for contact form reliability
  • Logging system for production monitoring
  • Domain migration and DNS updates

Task Planning: Each requirement became specific implementation tasks with dependencies mapped out. Database migration before environment setup, logging before deployment, etc.

Agentic Implementation: Rather than me writing everything manually, Kiro generated the code, configurations, and deployment scripts. I reviewed and adjusted, but didn't start from scratch.

What This Approach Caught

The structured flow identified problems I would have hit later:

  • SQLite syntax differences that would have broken in production
  • Missing environment access patterns for Cloudflare Workers
  • Need for graceful fallbacks when services fail

Without the upfront design work, I would have discovered these issues during deployment or worse, in production. The agentic implementation meant I got working code faster, but the real value was the systematic thinking that prevented problems.

Lessons Learned

What Went Well

  • OpenNext.js adapter made the Next.js migration straightforward
  • Wrangler CLI provided excellent tooling for database and deployment management
  • Cloudflare's documentation was comprehensive and accurate

Challenges

  • Environment access in server actions required research to get right
  • SQLite differences from PostgreSQL needed schema adjustments
  • Local development setup took some configuration to match production

Would I Do It Again?

Absolutely. The combination of cost savings, improved reliability, and better performance makes this migration a clear win. The initial time investment paid off immediately.

Recommendations

If you're considering a similar migration:

  1. Start with infrastructure: Set up D1 and KV before touching code
  2. Plan your fallback strategy: Don't rely on a single point of failure
  3. Test thoroughly: The environment differences between platforms require careful testing
  4. Use the right tools: Wrangler CLI and GitHub Actions make deployment seamless

For most small to medium applications, Cloudflare Workers provides better value and reliability than traditional hosting platforms. The edge-first architecture and integrated services create a compelling alternative to the typical Vercel + external database setup.

The migration took about a day of focused work, but the ongoing benefits in cost, performance, and reliability make it one of the best technical decisions I've made for CTK Advisors.

Share this post