# Vercel migration guide: practical steps to switch over from self-hosting (AWS/EC2/Netlify) with zero downtime

> A practical guide to migrating a Next.js app from self-hosting (AWS EC2/ECS/Amplify) or Netlify to Vercel with zero downtime. With official-compliant steps, it explains discerning migratability, moving environment variables and secrets, replacing stateful dependencies (DB/files/cron), parallel verification on preview, DNS cutover and rollback, and post-migration cost optimization.

- Published: 2026-06-28
- Author: 友田 陽大
- Tags: Vercel, Next.js, インフラ, アーキテクチャ設計, CI/CD, コスト最適化, DX
- URL: https://tomodahinata.com/en/blog/vercel-migration-aws-self-hosted-to-vercel-guide
- Category: Vercel in production
- Pillar guide: https://tomodahinata.com/en/blog/vercel-production-platform-guide

## Key points

- The key to migration is 'don't switch all at once.' Verify in parallel with production using Vercel preview deployments, and make the DNS cutover the last move. Make a configuration where, on a problem, you can roll back instantly just by reverting DNS.
- Discerning migratability is the first gate. Stateless, standard Next.js is easy. 'Server-premised' dependencies like local file writes, resident processes, WebSocket servers, and in-VPC dedicated DBs need a design replacing them with Blob/external DB/Workflows/Marketplace.
- Move environment variables organized into the three environments (production/preview/development) with vercel env add. Don't put secrets in NEXT_PUBLIC_. For external clouds, go OIDC-keyless rather than placing long-lived keys. Don't leave secrets in code.
- DB connections are prone to exhaustion on serverless, so move to pooled connections (Neon, etc.). Local files to Vercel Blob, periodic execution to Cron Jobs, long-running processing to Workflows/queues. Redesign where state lives by use.
- For cutover, shorten the TTL in advance → verify on preview → promote to production → switch DNS → monitor with x-vercel-cache and logs. After migration, optimize cost on the cache-design and Active CPU billing premise.

---

"I run Next.js on EC2 or ECS, or Netlify, but I'm tired of the operational hassle and slow deploys. I want to move to Vercel. But I don't want downtime" — this consultation is truly common. Migration is **zero-downtime as long as you don't get the method wrong**, and rollback is instant. Conversely, overlook a stateful dependency and you stall in production.

This article summarizes the **practical steps to migrate from self-hosting (AWS/EC2/ECS/Amplify) or Netlify to Vercel with zero downtime**, faithful to the [Vercel official documentation](https://vercel.com/docs). For the full picture of production operations at the destination, see the [Vercel production-operations guide](/blog/vercel-production-platform-guide).

---

## The big principle of migration: make the DNS cutover last

The most common migration incident is "point the production domain at Vercel all at once, and it doesn't work." The iron rule to avoid this is —

```
①Vercelにデプロイ（プレビューURLで動く状態を作る）
   ↓
②プレビューで本番同等の検証（並走）
   ↓
③本番ブランチへPromote（まだ独自ドメインは旧基盤）
   ↓
④DNSを切り替える（最後の一手）
   ↓
⑤問題があればDNSを戻すだけ＝即ロールバック
```

By making DNS the **last move**, you can verify as much as you want before switching, and on a problem you can return to the old platform just by reverting DNS.

---

## Step 0: discern migratability (most important)

Before running the code, **take inventory of "server-premised dependencies."** Skip this and you stall in production. Since Vercel is a stateless serverless platform, the following need replacing.

| Dependency on the old platform | Replacement on Vercel |
|---|---|
| **Local file writes** (upload storage, generated PDFs) | [Vercel Blob](/blog/vercel-storage-blob-edge-config-marketplace-guide) (client upload) |
| **Resident process / background worker** | [Cron Jobs](/blog/vercel-functions-fluid-compute-streaming-cron-guide) / Queues / Workflows |
| **Long-running processing** (batch, video conversion, >a few minutes) | Vercel Workflows (unlimited execution time) / external jobs |
| **Resident WebSocket server** | SSE / an external real-time platform (Ably/Pusher, etc.) / a separate resident platform |
| **In-VPC dedicated DB (no public endpoint)** | Make it a public + authenticated endpoint, or Marketplace (Neon, etc.), Secure Compute (Ent) |
| **In-memory session / rate limiting** | Redis (Upstash) / Edge Config / external store |
| **A huge monolith (>250MB bundle)** | Split features, or leave the target on a separate platform |

> **Judgment**: standard Next.js (App Router, stateless, external DB) is **easy** to migrate. Conversely, the deeper the dependence on "writing files locally" and "resident processes," the more the migration involves **redesign.** Honestly evaluating this divides success from failure.

---

## Step 1: connect the project to Vercel

Git integration is the shortest path. Import the repository, and a preview is created per push.

```bash
npm i -g vercel@latest
vercel login
vercel link            # 既存プロジェクトに紐付け（or 新規作成）
vercel                 # プレビューデプロイを作成 → 固有URLで動作確認
```

The framework is auto-detected, but confirm the **build command, output directory, and root directory** (especially for a monorepo) in settings. For a monorepo, watch the [Turborepo](/blog/pnpm-turborepo-monorepo-architecture-type-coverage-guide) root settings.

---

## Step 2: move environment variables and secrets

Move the old platform's environment variables, organized, into Vercel's **three environments (production / preview / development).**

```bash
# 本番のシークレットを production だけに登録
vercel env add DATABASE_URL production
vercel env add STRIPE_SECRET_KEY production

# プレビューには本番と別の値（ステージングDB等）を
vercel env add DATABASE_URL preview

# ローカル開発用をローカルへ同期
vercel env pull .env.local
```

Essential checks when moving ([the environment-variables guide](/blog/vercel-environment-variables-secrets-oidc-management-guide)):

- **Don't put secrets in the `NEXT_PUBLIC_` prefix** (exposed to the browser). API keys without the prefix.
- Environment-variable changes **apply only to new deployments** (existing deployments don't change).
- A good chance to switch external clouds (AWS S3/SES, etc.) to **OIDC keyless** rather than placing long-lived access keys.
- The total size is up to **64KB per deployment.**

---

## Step 3: replace stateful dependencies

### Local files → Vercel Blob

Upload files or generated PDFs saved on the EC2 disk go to Blob. User uploads via **client upload** (not through the server, avoiding transfer billing and the 4.5MB limit).

```ts
// 旧：fs.writeFileSync(path, buffer)
// 新：Vercel Blob（private で保存し、関数経由で配信）
import { put } from "@vercel/blob";
const blob = await put(`uploads/${id}.pdf`, buffer, {
  access: "private",
  addRandomSuffix: true,
});
```

### DB connections → prevent exhaustion with pooled connections

Under serverless concurrency, a "1 connection = 1 process" Postgres exhausts. Move to **pooled connections** (Neon's pooled endpoint, etc.).

```ts
// Neon の pooled 接続（サーバーレスでは必須級）
import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!); // pooled エンドポイント
```

For the logic of connection exhaustion, see [PgBouncer / connection pooling](/blog/postgresql-connection-pooling-pgbouncer-serverless-guide). For an in-VPC dedicated DB, make it a public + authenticated endpoint, or consider Enterprise Secure Compute.

### cron / resident worker → Cron Jobs / Workflows

```json
// 旧：crontab や常駐デーモン → vercel.json の crons
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "crons": [{ "path": "/api/cron/nightly", "schedule": "0 0 * * *" }]
}
```

Always protect cron with `CRON_SECRET`, and send long-running processing to Workflows/queues ([Functions guide](/blog/vercel-functions-fluid-compute-streaming-cron-guide)).

---

## Step 4: parallel verification on preview

Before cutting DNS, do **production-equivalent verification** on preview (or Vercel's `*.vercel.app` production URL).

- Run **E2E (Playwright)** against the preview URL ([E2E design](/blog/playwright-e2e-testing-production-design-guide)).
- Manually confirm the main paths (login, payment, upload, search).
- Confirm with `x-vercel-cache` that caching is `HIT`/`PRERENDER` as intended ([caching](/blog/vercel-caching-isr-cache-components-ppr-guide)).
- If needed, **run the old platform and Vercel in parallel** for a period, flowing only part of the traffic to Vercel to compare (DNS weighting, or a frontend flag for staged migration).

---

## Step 5: DNS cutover and rollback

Finally, switch the production domain.

```
事前：DNSレコードのTTLを短く（例 300秒）しておく（切替・巻き戻しを速く）
  ↓
Vercelにカスタムドメインを追加し、指示どおりA/CNAMEレコードを設定
  ↓
本番デプロイをPromote（Rolling Releases併用なら段階配信）
  ↓
DNSを切り替え → 伝播を監視（dig / 監視ツール）
  ↓
問題があれば：DNSを旧基盤に戻す（即ロールバック）
            or Vercel側はInstant Rollbackで前デプロイへ
```

- Shortening the TTL in advance makes both switching and reverting fast.
- A Vercel-side defect goes to the previous deployment immediately with **Instant Rollback.** To revert the whole platform, revert DNS.
- With Rolling Releases + Skew Protection, you can spread the Vercel deployment in stages even in production ([deployments](/blog/vercel-deployments-cicd-rollback-rolling-releases-guide)).

---

## Step 6: post-migration optimization

Migration's goal isn't "it works."

- **Cache design**: move pages that were SSR on the old platform to ISR/CDN cache. Measure `HIT` with `x-vercel-cache` ([caching](/blog/vercel-caching-isr-cache-components-ppr-guide)).
- **Cost**: on the Active CPU billing premise, separate heavy CPU processing and match memory/`maxDuration` to the use ([cost](/blog/vercel-cost-active-cpu-pricing-optimization-guide)).
- **Security**: protect the entrance with WAF/BotID and migrate to OIDC keyless ([Firewall](/blog/vercel-firewall-waf-botid-ddos-security-guide)).
- **Observability**: monitor functions, error rate, and CWV in Observability ([observability](/blog/vercel-observability-monitoring-speed-insights-log-drains-guide)).

---

## Migration checklist

- [ ] Take inventory of **server-premised dependencies** (files/resident/long-running/in-VPC DB) and design replacements
- [ ] Organize environment variables into three environments, no secrets in `NEXT_PUBLIC_`, understand changes apply only to new deployments
- [ ] DB with **pooled connections**, local files with **Blob**, cron with **Cron Jobs**
- [ ] Verify **E2E, main paths, and `x-vercel-cache`** on preview
- [ ] **Shorten the DNS TTL in advance**, configure the custom domain
- [ ] Prepare **monitoring** after cutover and the **revert procedure** for DNS/Instant Rollback
- [ ] After migration, optimize **caching, cost, security, and observability**

---

## Summary

Migration to Vercel can proceed safely with zero downtime if you keep the two points **"cut DNS last" and "replace stateful dependencies first."**

1. **Discern migratability honestly** (server-premised dependencies are key)
2. Cut DNS after **parallel verification** on preview
3. Files → Blob, DB → pooled, cron → Cron Jobs, long-running → Workflows
4. A **TTL shorten → Promote → DNS switch → monitor → can revert** configuration
5. After migration, finish **caching, cost, security, and observability**

From discerning migratability through redesigning stateful dependencies, zero-downtime cutover, and post-migration cost optimization, I accompany you as a project.

> This article is based on the [Vercel official documentation](https://vercel.com/docs) (environment variables, Blob, Cron, domains, as of June 2026). Specs are updated, so confirm the latest values officially at production adoption.
