← Back to blog
Admin 9 min read

Migrate from AWS EC2 to Cloudflare Workers in 2026

Migration Guide

Migrate from AWS EC2 to Cloudflare Workers

Move your web app from EC2, RDS, and S3 to the Cloudflare Workers Stack. Less infrastructure, better performance, lower cost.

TL;DR — What this guide covers

6 steps DNS → R2 (files) → D1 (database) → Workers (compute) → cutover → cleanup. Each step is independent.
Typical savings From $60–110/mo on AWS to $0–5/mo on Cloudflare — with faster global performance.
After migrating Use MyD1 to browse, query, and manage your D1 databases visually.

Still running EC2 + RDS + S3? You're paying $50–200/month for a stack that's slower than a free alternative. The Cloudflare Workers Stack does the same job — globally, with less ops, for $0–5/month.

This guide walks you through leaving AWS, service by service. No rush — each step is independent. Start wherever you want.

From
EC2 + RDS + S3
CloudFront + ALB + ACM
To
Workers + D1 + R2
Pages + automatic CDN/SSL

The Service Mapping

Here's what replaces what:

EC2 / Lambda
Workers (compute)
RDS / Aurora
D1 (edge SQLite)
S3
R2 ($0 egress)
CloudFront
Built-in CDN (automatic)
Route 53
Cloudflare DNS (fastest)
ACM + ALB
Automatic SSL (free)

Before You Start

You'll need:

  • A Cloudflare account (free)
  • Wrangler CLI installed: npm install -g wrangler
  • Your domain added to Cloudflare (free — just change nameservers)
  • Access to your current AWS app's codebase
Incremental migration

You don't have to migrate everything at once. The steps below are ordered from easiest to hardest. You can migrate your files to R2 first while keeping your app on EC2, then move compute later. Each step is independent and can be done in its own time.

Step-by-Step Migration

Step 1 Move your DNS to Cloudflare ~15 minutes

This is the foundation for everything else — and it's free.

  1. Add your domain in the Cloudflare dashboard
  2. Cloudflare imports your existing DNS records automatically
  3. Update your nameservers at your registrar to point to Cloudflare
  4. Wait for propagation (usually minutes, sometimes up to 24 hours)

What you gain immediately: Cloudflare's CDN, free SSL, DDoS protection, and the fastest authoritative DNS — all without changing a single line of code. Your EC2 app still runs exactly as before, but now it's behind Cloudflare's network.

Step 2 Migrate files from S3 to R2 ~30 minutes

R2 uses the S3-compatible API, which means most S3 tools and libraries work with R2 out of the box. Migration is straightforward.

Create your R2 bucket:

wrangler r2 bucket create my-app-files

Copy your S3 data to R2. You can use rclone, the most popular tool for cloud-to-cloud file transfers:

# Install rclone
brew install rclone  # macOS

# Configure S3 source (follow prompts)
rclone config  # add your AWS S3 remote

# Configure R2 destination
# Use S3-compatible config with your R2 credentials
# (found in Cloudflare dashboard → R2 → Manage R2 API Tokens)

# Sync everything
rclone sync s3-remote:my-bucket r2-remote:my-app-files

Update your app code. Since R2 uses the S3 API, you usually just need to change the endpoint URL and credentials in your S3 client config:

// Before (AWS S3)
const s3 = new S3Client({ region: 'us-east-1' });

// After (Cloudflare R2)
const s3 = new S3Client({
  region: 'auto',
  endpoint: 'https://YOUR_ACCOUNT_ID.r2.cloudflarestorage.com',
  credentials: {
    accessKeyId: R2_ACCESS_KEY,
    secretAccessKey: R2_SECRET_KEY
  }
});

What you gain: Zero egress fees. If you were spending $50–500/month on S3 data transfer, that drops to $0 immediately.

Step 3 Migrate your database from RDS to D1 ~1–2 hours

This is the most involved step. You're moving from PostgreSQL or MySQL to SQLite on D1. The good news: for most web apps, the SQL differences are minimal.

Create your D1 database:

wrangler d1 create my-app-db

Export your existing schema and data. If you're on PostgreSQL:

# Export schema
pg_dump --schema-only my_database > schema.sql

# Export data
pg_dump --data-only --inserts my_database > data.sql

Adapt the schema for SQLite. Key differences to handle:

  • Replace SERIAL / AUTO_INCREMENT with INTEGER PRIMARY KEY AUTOINCREMENT
  • Replace TIMESTAMP / DATETIME with TEXT (SQLite stores dates as text)
  • Replace BOOLEAN with INTEGER (0/1)
  • Remove PostgreSQL-specific types (UUID, JSONB, ARRAY) — use TEXT with JSON strings instead
  • Remove CREATE EXTENSION, ALTER SEQUENCE, and other Postgres-specific statements

Apply to D1:

# Apply schema
wrangler d1 execute my-app-db --remote --file=schema.sql

# Import data
wrangler d1 execute my-app-db --remote --file=data.sql

Verify your data with MyD1: Open MyD1, connect to your D1 database, and visually browse your tables to confirm everything migrated correctly. It's much faster than running queries in the terminal. See how to browse D1 visually.

What if my database is too complex for SQLite?

If you have heavy use of stored procedures, complex joins across 20+ tables, or advanced PostgreSQL features (full-text search with custom dictionaries, PostGIS, etc.), D1 might not be the right fit. Consider Hyperdrive — Cloudflare's connection pooler that lets Workers connect to your existing PostgreSQL database with reduced latency. You keep RDS, but your compute moves to the edge. Read our D1 vs PostgreSQL comparison for guidance.

Step 4 Move your app from EC2 to Workers ~2–4 hours

This step depends on what framework you're using. The easiest path is using a framework with a Cloudflare adapter:

Set up your wrangler.toml:

name = "my-app"
compatibility_date = "2026-01-01"

[[d1_databases]]
binding = "DB"
database_name = "my-app-db"
database_id = "your-d1-id"

[[r2_buckets]]
binding = "R2"
bucket_name = "my-app-files"

Update your database queries. Replace your PostgreSQL/MySQL client with D1's API:

// Before (PostgreSQL with pg)
const result = await pool.query(
  'SELECT * FROM users WHERE id = $1', [userId]
);
const user = result.rows[0];

// After (Cloudflare D1)
const user = await env.DB
  .prepare('SELECT * FROM users WHERE id = ?')
  .bind(userId)
  .first();

Test locally:

wrangler dev

This runs your app locally with real D1 and R2 emulation. Test everything before deploying.

Deploy:

wrangler deploy

Your app is now live on 330+ locations worldwide.

Step 5 Point your domain and go live ~5 minutes

Since your DNS is already on Cloudflare (Step 1), switching traffic is simple:

  1. Go to your Cloudflare dashboard → Workers & Pages → your project → Custom Domains
  2. Add your domain (e.g., myapp.com)
  3. Cloudflare automatically updates DNS and provisions SSL
  4. Traffic now hits your Workers app instead of EC2

Zero downtime: DNS propagation is near-instant within Cloudflare's network. Your old EC2 instance still runs until you shut it down — keep it as a fallback for a few days, then terminate it when you're confident.

Step 6 Shut down AWS resources ~15 minutes

Once you've verified everything works on Cloudflare:

  • Terminate your EC2 instance(s)
  • Delete the RDS database (after a final backup)
  • Empty and delete S3 buckets
  • Remove CloudFront distributions
  • Delete ALB, target groups, and security groups
  • Clean up unused IAM roles and policies
  • Delete the Route 53 hosted zone
  • Review your next AWS bill — it should be $0

What You'll Save

90%+ Typical cost reduction
3–10x Faster global response times
$0 Egress fees, forever

Here's what a typical migration looks like in real numbers:

ServiceAWS monthly costCloudflare monthly cost
Compute (EC2 t3.small)$15–30$0 (free tier) or $5
Database (RDS t3.micro)$15–25$0 (D1 free tier)
File storage (S3, 50GB)$1–5$0.75
Data transfer (100GB egress)$9$0
CDN (CloudFront)$5–20$0 (built-in)
SSL (ACM + ALB)$16+ (ALB hourly)$0 (automatic)
DNS (Route 53)$0.50–2$0
Total$62–112/month$0–5.75/month
Annual savings$670–1,275/year

Common Migration Concerns

What about Node.js compatibility?

Workers run on V8, not Node.js. Most npm packages work fine, but anything that relies on fs, child_process, or native C++ addons won't work. Workers has built-in support for crypto, fetch, streams, and most Web APIs. Check the Workers Node.js compatibility docs for specifics.

What about WebSockets / real-time?

Workers support WebSockets natively, and Durable Objects give you stateful, real-time coordination (chat rooms, collaborative editing, game servers). For most real-time use cases, this replaces what you'd use EC2 + Redis for.

What about cron jobs / scheduled tasks?

Workers support Cron Triggers — define scheduled tasks in your wrangler.toml and they run on Cloudflare's infrastructure. No more EC2 crontabs or Lambda scheduled events with CloudWatch rules.

Can I migrate gradually?

Absolutely. The recommended order is: DNS first (immediate benefits, zero risk), then R2 (easy S3 migration), then D1 (schema adaptation), then Workers (the biggest change). Each step is independent. You can run your app on EC2 while already using R2 for storage.

After the Migration

Once you're on the Cloudflare Workers Stack, here's what changes:

  • Deployments take seconds, not minutes. wrangler deploy and you're live globally.
  • No servers to patch or maintain. No OS updates, no security patches, no SSH.
  • No capacity planning. Workers scale automatically to any traffic level.
  • Your AWS bill goes to $0. Or close to it.
  • Your app is faster for every user, everywhere in the world. Not because of optimization — because of architecture.

Use MyD1 to keep an eye on your D1 databases after migration. Browse tables, run queries, check data integrity — all from a native desktop app instead of the terminal.

Ready to Migrate?

The best time to move off EC2 was last year. The second best time is today. Start with Step 1 — move your DNS to Cloudflare. It's free, it's instant, and it gives you immediate performance and security benefits with zero risk.

Then work through the rest at your own pace. Every step saves you money and makes your app faster.

Once you're on D1, download MyD1 to manage your databases visually — browse tables, run queries, and verify your migration in seconds.

Related: Why AWS Is So Slow · AWS EC2 vs Cloudflare Workers Stack · Build a Full-Stack App for Free on Cloudflare · D1 vs MySQL vs PostgreSQL