"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. For the full picture of production operations at the destination, see the Vercel production-operations 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 (client upload) |
| Resident process / background worker | Cron Jobs / 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.
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 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).
# 本番のシークレットを 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):
- 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).
// 旧: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.).
// 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. For an in-VPC dedicated DB, make it a public + authenticated endpoint, or consider Enterprise Secure Compute.
cron / resident worker → Cron Jobs / Workflows
// 旧: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).
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).
- Manually confirm the main paths (login, payment, upload, search).
- Confirm with
x-vercel-cachethat caching isHIT/PRERENDERas intended (caching). - 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).
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
HITwithx-vercel-cache(caching). - Cost: on the Active CPU billing premise, separate heavy CPU processing and match memory/
maxDurationto the use (cost). - Security: protect the entrance with WAF/BotID and migrate to OIDC keyless (Firewall).
- Observability: monitor functions, error rate, and CWV in Observability (observability).
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-cacheon 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."
- Discern migratability honestly (server-premised dependencies are key)
- Cut DNS after parallel verification on preview
- Files → Blob, DB → pooled, cron → Cron Jobs, long-running → Workflows
- A TTL shorten → Promote → DNS switch → monitor → can revert configuration
- 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 (environment variables, Blob, Cron, domains, as of June 2026). Specs are updated, so confirm the latest values officially at production adoption.