Vercel is the smoothest way to ship Next.js. Connect a repo, push, and you have a URL with HTTPS. So why would anyone leave?
Usually for one of three reasons: you want the app on infrastructure you control, you want several projects on one server you already pay for, or you need server-side behaviour (long-running processes, background jobs, file storage) that fits a normal Node server better than serverless functions.
This guide shows how to self-host a Next.js app on a Linux VPS, first the manual way, then the shortcut with ShipOwn.
What you lose and keep when leaving Vercel
Be honest about the trade:
| Vercel feature | On a VPS |
|---|---|
next build and next start
|
Same, Next.js runs as a normal Node server |
| HTTPS and domains | Nginx + Certbot |
| Preview deployments per PR | Not automatic, you'd build this yourself |
| Global edge network | One region (your server), add a CDN if needed |
| Instant rollback | Release directories, or a tool that keeps them |
| Image optimisation | Works with next start on the server |
If preview deployments and edge locations are core to your workflow, stay on Vercel. If you mostly need "a fast server running my app on my domain", a VPS is enough.
Prerequisites
- A Linux VPS (Ubuntu or Debian is easiest) with SSH access.
- A domain whose A record points to the server's IP.
- Your Next.js repo on GitHub, or locally.
The manual way
Step 1: Prepare the server
ssh root@your-server-ip
adduser deploy && usermod -aG sudo deploy
apt update && apt install -y nginx git
# install Node via your preferred method (nvm or NodeSource), then:
npm i -g pm2
Step 2: Build the app
su - deploy
git clone https://github.com/you/next-app.git && cd next-app
cp .env.example .env.production # fill in real values
npm ci && npm run build
Remember that NEXT_PUBLIC_ variables are baked in at build time. If you change one, you have to rebuild, not just restart.
Step 3: Run it with PM2
pm2 start npm --name next-app -- start -- -p 3000
pm2 save && pm2 startup
Step 4: Nginx reverse proxy
server {
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
sudo ln -s /etc/nginx/sites-available/next-app /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Step 5: HTTPS
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d app.example.com
Step 6: Plan your rollback
This is the step people skip. If you git pull && npm run build in place and the build fails halfway, your live app can break. A safer pattern is to build each release in its own directory and switch a symlink only after it works.
That's six steps and a fair amount to remember, every time.
The ShipOwn way
ShipOwn is a desktop app (Windows, macOS, Linux) that does the same job over SSH, without installing a deployment agent on your server.
- Connect the server. Enter host, port, user, and key or password. ShipOwn tests the connection before saving.
- Pick the source. Choose your GitHub repo and one of its real branches, or a local folder.
- Review the plan. ShipOwn detects Next.js and proposes install, build and start commands, the port, the domain and the target. Every value is editable; nothing runs until you approve.
- Watch it build. Build on the server, or build on your PC and upload the verified output. Output streams live.
- Operate it. One page shows the live URL, the commit that's deployed, how many commits the branch is ahead, health, logs, redeploy and rollback.
Behind the scenes it covers the manual steps above:
- Runs the app under PM2 with the port, health check and restart policy set.
- Writes the Nginx virtual host, tests it before reloading, and restores the old config if Nginx rejects it.
- Asks Certbot for a certificate when you want HTTPS, and shows certificate expiry in the app.
- Puts every deploy in its own release directory. The live pointer moves only after the build and health check pass, and a failed deploy restores the previous release.
Common Next.js-on-VPS problems
502 Bad Gateway. Nginx can't reach the app. Check pm2 status and that the port in Nginx matches the port Next.js listens on.
Build killed / out of memory. Small VPS plans can run out of RAM during next build. Add swap, or build on your own machine. ShipOwn's PC build mode exists for exactly this case.
Env var changes not showing. If it's a NEXT_PUBLIC_ variable, rebuild.
App down after reboot. Run pm2 save and pm2 startup.
When you hit something you can't place, ShipOwn's AI terminal can help. You describe the problem, and using your own Anthropic, OpenAI or DeepSeek key it proposes one shell command at a time with a reason. You approve each one, and destructive commands are always held for confirmation.
Is it worth it?
Self-hosting Next.js makes sense when:
- You already pay for a VPS with spare capacity.
- You want app and data on infrastructure you control.
- You're fine without per-PR preview URLs.
It's not worth it if your team depends on Vercel's previews and edge network.
FAQ
Does ShipOwn deploy on every push like Vercel? No. It tells you when a branch has moved ahead and how many commits are waiting, and you press deploy.
Do I need Docker for Next.js? No. ShipOwn runs Next.js directly under PM2. Docker is optional.
Which server OS works? ShipOwn handles Ubuntu, Debian, the RHEL family and Alpine. Check the supported environments page for details.
Conclusion
You can keep most of the Vercel experience (build, URL, HTTPS, rollback) on a server you own. Do it by hand once to understand it, then let ShipOwn repeat it safely. There's a free 30-day trial with no card.
Written by Vishvajeet Shukla, full stack developer and creator of ShipOwn.
Top comments (0)