t
Temps

How to Migrate from Vercel to Self-Hosted: Complete Guide

How to Migrate from Vercel to Self-Hosted: Complete Guide

January 17, 2026 (1mo ago)

Temps Team

Written by Temps Team

Last updated January 17, 2026 (1mo ago)

Moving your Next.js application from Vercel to a self-hosted platform saves money and gives you infrastructure control. This guide covers the exact steps to migrate, what breaks during migration, and how to fix it.

Why Teams Leave Vercel

Three patterns trigger most Vercel departures:

  1. Cost scaling — Bills that start at $20 grow to $500+ as teams and traffic increase
  2. Vendor lock-in — Features like Edge Middleware and ISR create Vercel-specific dependencies
  3. Data control — Compliance requirements demanding data stays on owned infrastructure

If any of these sound familiar, migration makes sense.


Before You Migrate: Compatibility Check

What Works Everywhere

These Next.js features transfer to any platform without changes:

  • Static pages (SSG)
  • Server-side rendering (SSR)
  • API routes
  • Image optimization (with configuration)
  • Middleware (basic redirect/rewrite logic)
  • Incremental Static Regeneration (ISR)

What Needs Adjustment

These Vercel-specific features require alternatives:

Vercel FeatureAlternative
Edge MiddlewareStandard Node.js middleware
Edge RuntimeNode.js runtime
Vercel AnalyticsTemps built-in / Plausible / Fathom
Vercel KVRedis (Upstash, self-hosted)
Vercel PostgresAny PostgreSQL (Neon, Supabase, self-hosted)
Vercel BlobS3-compatible storage (MinIO, Cloudflare R2)
@vercel/ogStandard ImageResponse from Next.js

What Doesn't Transfer

  • Vercel's specific edge locations and CDN configuration
  • Vercel-proprietary analytics dashboards
  • Speed Insights (use Core Web Vitals monitoring instead)

Migration Path 1: Vercel → Temps

Temps provides the closest experience to Vercel with self-hosted infrastructure. Most apps migrate without code changes.

Step 1: Export Environment Variables

From Vercel dashboard:

  1. Go to Project Settings → Environment Variables
  2. Export all variables or note them manually
# Or use Vercel CLI
vercel env pull .env.local

Step 2: Set Up Temps

# Install Temps on your server
curl -fsSL https://temps.sh/deploy.sh | bash

# Login to your Temps instance
bunx @temps-sdk/cli login

Step 3: Import Your Project

  1. Open Temps dashboard (default: http://your-server:3000)
  2. Click "New Project" → "Import from GitHub"
  3. Select your repository
  4. Add environment variables from Step 1
  5. Deploy

Step 4: Update DNS

Replace Vercel's DNS records with Temps-provided values:

# Remove
CNAME  @     cname.vercel-dns.com
CNAME  www   cname.vercel-dns.com

# Add
CNAME  @     your-temps-domain.temps.sh
CNAME  www   your-temps-domain.temps.sh

Step 5: Verify

  • Site loads correctly
  • All pages render (SSR, SSG, API routes)
  • Forms and authentication work
  • Images load properly
  • Analytics capturing data

Migration time: 15-30 minutes


Migration Path 2: Vercel → Docker + VPS

For teams wanting maximum control, Docker deployment offers complete infrastructure ownership.

Step 1: Enable Standalone Output

Add to next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: "standalone",
};

module.exports = nextConfig;

Step 2: Create Dockerfile

# Build stage
FROM node:20-alpine AS builder
WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci

# Build application
COPY . .
RUN npm run build

# Production stage
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production

# Copy built application
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"]

Step 3: Set Up VPS

On DigitalOcean, Hetzner, or your preferred provider:

# Install Docker
curl -fsSL https://get.docker.com | sh

# Clone your repo
git clone https://github.com/your/repo.git
cd repo

# Build image
docker build -t my-nextjs-app .

# Run container
docker run -d \
  --name my-app \
  -p 3000:3000 \
  --env-file .env.production \
  my-nextjs-app

Step 4: Set Up Reverse Proxy (nginx)

server {
    listen 80;
    server_name yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

Step 5: SSL with Let's Encrypt

# Install certbot
apt install certbot python3-certbot-nginx

# Get certificate
certbot --nginx -d yourdomain.com

Migration time: 1-2 hours


Code Changes You May Need

Removing Vercel Analytics

Before:

// app/layout.tsx
import { Analytics } from "@vercel/analytics/react";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Analytics />
      </body>
    </html>
  );
}

After (with Temps or Plausible):

// app/layout.tsx
// Temps: Analytics included automatically, no code needed

// Or with Plausible:
import Script from "next/script";

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Script
          defer
          data-domain="yourdomain.com"
          src="https://plausible.io/js/script.js"
        />
      </body>
    </html>
  );
}

Replacing Edge Runtime

Before (Vercel Edge):

// app/api/hello/route.ts
export const runtime = "edge";

export async function GET() {
  return Response.json({ message: "Hello" });
}

After (Node.js):

// app/api/hello/route.ts
// Remove the runtime declaration - uses Node.js by default

export async function GET() {
  return Response.json({ message: "Hello" });
}

Replacing Vercel KV

Before:

import { kv } from "@vercel/kv";

export async function GET() {
  const value = await kv.get("key");
  return Response.json({ value });
}

After (with Upstash or self-hosted Redis):

import { Redis } from "@upstash/redis";

const redis = new Redis({
  url: process.env.REDIS_URL,
  token: process.env.REDIS_TOKEN,
});

export async function GET() {
  const value = await redis.get("key");
  return Response.json({ value });
}

Replacing Vercel Postgres

Before:

import { sql } from "@vercel/postgres";

export async function GET() {
  const { rows } = await sql`SELECT * FROM users`;
  return Response.json({ users: rows });
}

After (with any PostgreSQL):

import { Pool } from "pg";

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
});

export async function GET() {
  const { rows } = await pool.query("SELECT * FROM users");
  return Response.json({ users: rows });
}

Image Optimization

Next.js image optimization works on all platforms. You may need to configure allowed domains:

// next.config.js
const nextConfig = {
  output: "standalone",
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "**.yourdomain.com",
      },
    ],
  },
};

module.exports = nextConfig;

Post-Migration Checklist

Functionality

  • All pages load correctly (SSR, SSG, static)
  • API routes respond properly
  • Authentication flows work
  • Forms submit successfully
  • File uploads function
  • Images display and optimize
  • Middleware redirects/rewrites work

Performance

  • TTFB (Time to First Byte) acceptable
  • Core Web Vitals passing
  • No significant cold starts
  • Caching working correctly

Operations

  • SSL certificate valid and auto-renewing
  • Environment variables configured
  • Logging capturing errors
  • Monitoring/alerting set up
  • Backup strategy in place

DNS

  • Domain pointing to new infrastructure
  • Old Vercel DNS records removed
  • SSL certificate covers all subdomains
  • Redirect from www to apex (or vice versa)

Common Migration Issues

Issue: Build fails with "Module not found"

Cause: Vercel auto-installs some dependencies that aren't in your package.json

Fix: Add missing dependencies explicitly:

npm install sharp  # For image optimization

Issue: API routes return 404

Cause: Incorrect build output or routing configuration

Fix: Ensure output: 'standalone' is set and rebuild

Issue: Images not loading

Cause: Image optimization requires Sharp package

Fix:

npm install sharp

Issue: Environment variables not found

Cause: Variables not set in new environment

Fix: Verify all env vars are configured. Use .env.production for Docker or dashboard for Temps.

Issue: Middleware not executing

Cause: Edge runtime not supported

Fix: Remove runtime: 'edge' if present. Standard middleware works on all platforms.


Cost Savings After Migration

Typical Before/After

MetricVercelSelf-Hosted
5-person team$100/mo$0
500GB bandwidth$160/mo$0
Sentry (errors)$29/moIncluded*
Analytics$19/moIncluded*
Monthly total$308$40-50

*With Temps. Docker requires separate tools.

Annual Savings

Team SizeVercel AnnualSelf-Hosted AnnualSavings
3 devs$1,500+$400-600$900+
10 devs$3,600+$600-900$2,700+
25 devs$8,000+$900-1,200$6,800+

When to Stay on Vercel

Migration isn't always the right choice. Stay on Vercel if:

  • Zero DevOps tolerance — Your team has no one to manage infrastructure
  • Heavy Edge usage — You rely on Edge Middleware and global edge functions
  • Enterprise features — You need Vercel's SOC2, HIPAA, or enterprise support
  • Convenience over cost — Time is more valuable than the savings

Next Steps

Ready to Migrate?

  1. Audit your app — Check for Vercel-specific features using this guide
  2. Choose your platform — Temps for easiest migration, Docker for maximum control
  3. Test in staging — Deploy to a test environment first
  4. Schedule migration — Plan DNS cutover during low-traffic period
  5. Monitor closely — Watch for issues in the first 48 hours

Get Help


This guide covers Next.js 14+ with App Router. Pages Router apps follow similar patterns. Always test thoroughly before production migration.

#migration#vercel#self-hosted#docker#next.js#deployment