Migrate Your Next.js App from Vercel to Your Own Infrastructure

Introduction

Vercel is a fantastic platform for getting a Next.js app running in minutes. But as your application grows, the trade-off for that initial simplicity becomes clear: rising costs, platform lock-in, and a lack of control over your infrastructure.

This guide shows you how to self-host your Next.js application on your own VPS using Disco. The result? The same magical git-push-to-deploy workflow you love, with predictable costs (from $5/month) and the freedom to run on any cloud provider you choose.

This is an advanced tutorial. It assumes you're comfortable provisioning a server and setting a DNS record. In exchange for that one-time setup, you get a powerful, open-source platform that you own and control.


Step 1 - Create a Server

You need a VPS to host your application. You can use any cloud provider:

Recommended providers:

  • Digital Ocean - Easy to use, global locations
  • AWS Lightsail or EC2 - If you're already on AWS
  • Hetzner Cloud - Great EU performance

Minimum requirements:

  • 2 GB RAM minimum (more is better for Next.js builds)
  • Ubuntu 24.04 LTS

Create your server using your provider's dashboard and note down the IP address.

Step 2 - Configure DNS Settings

You need to set up two domains: one for your Disco server and one for your application.

Go to your domain registrar's DNS settings and create two A records:

A Server domain (e.g., disco.example.com):

  • Name: disco
  • Type: A
  • Value: Your server IP (e.g., 203.0.113.1)
  • TTL: 3600 (or leave default)

And an Application domain (e.g., app.example.com):

  • Name: app
  • Type: A
  • Value: Your server IP (e.g., 203.0.113.1)
  • TTL: 3600 (or leave default)

Example DNS configuration:

Type    Name    Value           TTL
A       disco   203.0.113.1     3600
A       app     203.0.113.1     3600

DNS propagation can take a few minutes to several hours. Verify that your DNS records are properly set up:

ping disco.example.com

If you're seeing that ping is hitting your server's IP, you are good to continue.

Step 3 - Install the Disco CLI

Important: Run this on your local machine, NOT on your server.

curl https://cli-assets.letsdisco.dev/install.sh | sh

Verify installation:

disco --version

Step 4 - Initialize Server with Disco

Initialize your server with Disco (this installs Docker, Caddy, and the Disco daemon) by running the following command on your own, local machine:

disco init root@disco.example.com

Replace disco.example.com with your server domain from Step 2.

What this does:

  • Installs Docker and Docker Swarm
  • Installs the Caddy web server (handles HTTPS automatically)
  • Installs the Disco daemon for deployment management

This process should take a few minutes.

Step 5 - Prepare Your Next.js Application

To deploy Next.js with Disco, you need to add a few files to your project:

  1. Configure Next.js for standalone mode in next.config.js or next.config.ts
  2. Add a Dockerfile for building your app (see below for an example repo)
  3. Add a disco.json configuration file (see below as well)

See the complete example: Next.js Disco Example Repository

The key changes are:

  • Setting output: 'standalone' in the Next.js config file
  • Using a multi-stage Docker build
  • Exposing port 3000 in disco.json

Step 6 - Connect to GitHub

6.1 - Create a GitHub Repository

If you haven't already:

  1. Go to github.com and create a new repository
  2. Initialize git and push your code:
# Initialize git if needed
git init

# Add all files
git add .

# Commit
git commit -m "Initial commit with Disco configuration"

# Add remote and push
git remote add origin git@github.com:YOUR_USERNAME/my-nextjs-app.git
git push -u origin main

6.2 - Connect Disco to GitHub

Install the Disco GitHub App to enable automatic deployments:

disco github:apps:add

This will open a browser window. Authorize the Disco GitHub App and select your repository.

Step 7 - Deploy the Application

Create a new project on your Disco server. This will automatically trigger the first deployment:

disco projects:add \
  --name my-nextjs-app \
  --github YOUR_GITHUB_USERNAME/my-nextjs-app \
  --domain app.example.com \
  HOSTNAME=0.0.0.0

Replace app.example.com with your application domain from Step 2, and YOUR_GITHUB_USERNAME/my-nextjs-app with the path to your GitHub repo.

The first build will take 2-5 minutes as Docker downloads base images and builds your application. Further deployments will be faster thanks to layer caching.

Step 8 - Test the Deployed Application

Once deployment completes, visit your domain:

open https://app.example.com

What to verify:

  • Homepage loads correctly
  • HTTPS certificate is valid (automatic via Let's Encrypt)
  • Images are loading
  • API routes work (test with /api/... endpoints)
  • Dynamic routes render correctly

Step 9 - Automatic Deployments

Now that everything is set up, any git push to your main branch will automatically trigger a deployment. Disco detects the push and immediately deploys your changes. You can follow the deployment output by running:

disco deploy:output --project my-nextjs-app

FAQ

What Next.js features work with self-hosting?

Standard Next.js features work perfectly: SSR, SSG, API Routes, App Router, Pages Router, middleware (runs on Node.js), and image optimization (using the sharp library).

What doesn't work?

Vercel-specific features like Edge Functions, Edge Middleware, and Vercel Analytics won't work. Use standard alternatives instead.

How do I set environment variables?

Use the Disco CLI:

disco env:set --project my-nextjs-app DATABASE_URL "postgresql://..."

The command above will automatically trigger a redeploy.

Do I need to modify my existing Next.js app?

Yes. The minimal changes are:

  • Add output: 'standalone' to next.config.js
  • Add a Dockerfile (see example repo)
  • Add disco.json configuration file (see repo as well)

What if my images aren't loading?

Verify sharp is installed (automatic in Next.js 15+). For Next.js 14 and below, add to package.json:

"sharp": "^0.33.0"

How can I improve global performance?

Consider adding a CDN like Cloudflare in front of your deployment for better global distribution and caching.

Need help?

If you have questions or run into issues: