Verdict at the top: for a typical small-to-medium Next.js project, the honest first answer is that a managed platform such as Vercel or Cloudflare handles most Next.js workloads with very little operational work. Self-host on a VPS when (a) you've outgrown the free tiers, (b) you want to keep the frontend, a Node backend and a database on the same machine for cost, or (c) you already run other services on the box and want to consolidate. For pure-static Next.js sites, shared cPanel or DirectAdmin hosting works well: output: 'export', upload out/ to public_html/, done. This guide covers each path honestly.
Next.js has three rendering modes (Static, SSR, ISR) and five practical hosting options: Vercel, Cloudflare, a Domain India VPS, the Domain India App Platform, and Domain India shared hosting with a static export. Vercel and Cloudflare suit most marketing sites and content-heavy apps; a VPS gives you control and room for other services; shared hosting is best kept for pure-static sites without API routes or middleware.
1. The five paths: when each is right
| Path | When to pick it | Monthly cost (typical small site) |
|---|---|---|
| Vercel (Hobby = free / Pro $20/mo per user) | Default for new Next.js projects; pure Next.js focus; team has no ops bandwidth | ₹0-1,700 |
| Cloudflare Workers or Pages (free / Workers Paid $5/mo) | Cost-conscious; want one bill for CDN + DNS + hosting; comfortable with Cloudflare's runtime limits | ₹0-450 |
| Domain India VPS | Self-host preferred; need to colocate with backend/DB; running multiple services | From ₹553 + GST |
| Domain India App Platform | Managed Node.js hosting without running a server yourself | From ₹100 + GST |
| Domain India shared (cPanel/DirectAdmin) | Pure-static export (no API routes, no SSR, no middleware) | See the cPanel and DirectAdmin plan pages |
The decision lands on the question: does your app need long-running Node-server features (SSR, API routes, middleware, ISR)?
- No — static export is great; pick whichever host you already use.
- Yes — pick Vercel, Cloudflare, the App Platform or a VPS. Shared hosting is built for small request/response apps, not a production Next.js server (see section 5).
2. Latency: the part most "X vs Y" articles fudge
For an Indian audience, the distance between the visitor and whatever answers the request matters most. Rough, illustrative numbers for Indian broadband:
| Path | India round trip to first byte |
|---|---|
| Cached response from a CDN edge in India (Vercel or Cloudflare) | 5-30 ms |
| Server-rendered response from a function region in India | 15-40 ms |
| Server-rendered response from a function region in the US | 200-300 ms |
| Origin server in India, no CDN | 20-50 ms |
| Origin server in Europe, no CDN | 130-180 ms |
| Any origin with Cloudflare in front | 5-15 ms (cached) / origin round trip (miss) |
Takeaway for Indian-audience sites: cached pages and assets are fast on any CDN with Indian edges. What differs is where your server-rendered pages and API routes run. On Vercel, check which function region your plan lets you choose and pick Mumbai if it is available; on your own server, put Cloudflare in front and cache what you can. If you don't know where your hosting server is, a ping or traceroute from India gives you a good idea.
3. Path 1: Vercel (the default)
Best when: you want zero ops overhead, your project is Next.js-centric, and you can stomach the pricing model.
The good: deploys from git, first-class Next.js support (Vercel maintains the framework), built-in image optimisation, free SSL, automatic preview deploys per pull request.
The catches:
- Hobby (free) tier excludes commercial use. If you're shipping a product, you're technically required to be on Pro ($20/mo per user, ~₹1,700). Vercel's enforcement is light-touch but the licence is clear.
- Function region matters. Static and cached content is served from Vercel's global CDN, which includes India, but server-rendered pages and API routes run in the function region you choose (the default is in the US). Check your plan's region options.
- Usage pricing is the gotcha. Pro includes a data transfer allowance and then charges per GB, at rates that vary by region. A medium-traffic site that goes viral can produce a large bill; set spend limits.
- Vendor lock-in is real. Vercel-specific features (ISR On-demand, Edge Config, Vercel KV) tie your app to their platform. Migrating later is painful.
Setup:
npm install -g vercel
cd my-next-app
vercel # interactive setup
vercel --prod # deploy productionThat's it. Push to git → auto-deploy. Custom domain via Vercel's dashboard.
When to graduate off Vercel: when usage bills keep growing, when you want to colocate with your own backend services, or when you have the ops capacity to self-host.
4. Path 2: Cloudflare Workers and Pages
Best when: you want low-cost hosting with Indian edge locations and your Next.js app fits within Cloudflare's runtime.
The good: Cloudflare has edge locations in several Indian cities (including Mumbai, Chennai, Delhi, Bengaluru, Hyderabad and Kolkata); static assets are served without bandwidth charges; custom domains and SSL are free; it integrates with Workers, D1, R2 and KV.
The catches:
- Use the OpenNext adapter. Cloudflare's current recommendation for full Next.js apps is the OpenNext adapter (
@opennextjs/cloudflare), which deploys to Workers. The older@cloudflare/next-on-pagesadapter only supported the edge runtime and is deprecated. Pure static exports can still go straight to Pages. - Not a full Node.js server. Workers support many Node.js APIs through compatibility flags, but not all. Native modules (for example
sharp) and anything usingchild_processwon't run; use Cloudflare Images or a custom image loader. - Free-tier limits. Workers requests, CPU time and build minutes have free allowances; check Cloudflare's pricing page for current numbers.
Setup: follow Cloudflare's Next.js guide for the OpenNext adapter, connect your GitHub repository, and deploy. Pushes to the repository can trigger new builds.
For Next.js apps that mostly use static generation with a few API routes, Cloudflare is excellent and very cheap. For apps that lean heavily on Node-only packages or advanced Next.js features, Vercel or your own server is the safer bet.
5. Path 3: a Domain India VPS (self-host)
Best when: you want infrastructure control, have multiple services to colocate, or want a fixed monthly cost regardless of traffic. A Domain India VPS is self-managed: you get full root access and you install, secure and back up everything yourself.
Build for production
Configure next.config.js for standalone output:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
// If using next/image with non-localhost domains:
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'cdn.yourdomain.com' },
],
},
};
module.exports = nextConfig;Build locally or in CI:
npm install --legacy-peer-deps # only if you have peer-dep conflicts
npm run buildStandalone output structure:
.next/standalone/server.js ← the entry point
.next/standalone/.next/...
.next/standalone/package.json
.next/static/ ← copy alongside
public/ ← copy alongsideDeploy package — what to ship
# On dev machine, build then copy these to VPS
rsync -avz --delete .next/standalone/ user@vps:/opt/myapp/
rsync -avz --delete .next/static/ user@vps:/opt/myapp/.next/static/
rsync -avz --delete public/ user@vps:/opt/myapp/public/Run via systemd
/etc/systemd/system/myapp.service:
[Unit]
Description=My Next.js App
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/myapp
Environment=NODE_ENV=production
Environment=PORT=3000
Environment=HOSTNAME=127.0.0.1
ExecStart=/usr/bin/node server.js
Restart=on-failure
User=myapp
Group=myapp
[Install]
WantedBy=multi-user.targetsudo systemctl enable --now myapp
sudo systemctl status myapp
sudo journalctl -u myapp -f # tail logssystemd is the right answer in 2026 — PM2 was popular but adds complexity for no real benefit on a single-host setup.
Reverse proxy with nginx
/etc/nginx/sites-available/myapp:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name yourdomain.com www.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://127.0.0.1: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;
}
location /_next/static/ {
alias /opt/myapp/.next/static/;
expires 365d;
access_log off;
add_header Cache-Control "public, immutable";
}
}sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginxOr skip nginx and use Caddy, which auto-handles SSL via Let's Encrypt:
yourdomain.com {
reverse_proxy 127.0.0.1:3000
handle_path /_next/static/* {
root * /opt/myapp/.next/static
file_server
}
}For most VPS deployments, Caddy is genuinely simpler than nginx + certbot.
Build memory — the most common deploy gotcha
Next.js builds are memory-hungry. On a small VPS with 2 GB of RAM, a medium-sized Next.js app can run out of memory during npm run build. Symptoms:
<--- Last few GCs --->
[12345:0x...] FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memoryThree fixes:
- Build elsewhere, deploy artefacts. Run
npm run buildin CI (GitHub Actions) or on your laptop, then rsync the standalone output. Most-recommended pattern. - Add swap on the VPS if you must build on the box:
bash sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab - Bump Node's heap limit (less reliable):
bash NODE_OPTIONS='--max-old-space-size=2048' npm run build
If the build still fails, build in CI or move to a larger VPS plan; the VPS page lists the RAM of each plan.
6. Path 4: the Domain India App Platform
The App Platform is managed hosting for apps: you deploy from GitHub with Deploy Now, or from CI with a deploy token, and the platform builds and runs the app. Node.js is detected automatically, so a Next.js app with standard build and start scripts is a natural candidate; any other stack needs your own Dockerfile. Every plan includes PostgreSQL and free SSL.
Things to know before you choose it:
- There is no automatic deploy on every push. Click Deploy Now, or call the deploy token from your CI pipeline.
- There is no SSH access, no Redis add-on and no WebSocket support. If your app needs any of these, use a VPS.
- Test your app there before you move production traffic. The getting started guide walks through the first deploy.
- 512 MB RAM per app
- 1 vCPU
- 5 GB NVMe SSD
- PostgreSQL Database
7. Path 5: static export to Domain India shared hosting
Best when: your Next.js app is genuinely static (no API routes, no SSR, no middleware), and you already have shared hosting at Domain India.
// next.config.js
const nextConfig = {
output: 'export',
images: {
unoptimized: true, // built-in optimiser disabled for static export
},
trailingSlash: true, // generates /about/index.html
};
module.exports = nextConfig;npm run build
# Creates out/ directory with static HTML/CSS/JSUpload the contents of out/ to public_html/ with the File Manager, FTP, or GitHub Actions to cPanel.
Domain India shared hosting runs the Apache web server and .htaccess rules work. For clean URLs, drop a .htaccess in public_html/:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1/index.html [L]What you lose on static export:
- API routes (
pages/api/*,app/api/*) — they don't exist; you'll need a separate backend or external service. - SSR (
getServerSideProps) — pages are fully pre-rendered at build time. - ISR (
revalidateingetStaticProps) — same; no runtime regeneration. - Image optimisation via
next/image— served as-is unless you use a third-party loader (Cloudinary, imgix). - Middleware — edge functions don't run.
- Dynamic routes that can't be enumerated at build time —
/users/[id]with arbitrary IDs needs SSR.
If your app is a marketing site, blog, documentation, or portfolio with no user-specific server-side rendering, static export is excellent — fast, cheap, no operational overhead.
What about the Node.js tool on shared hosting?
cPanel and DirectAdmin both have a Node.js app tool (Setup Node.js App), which runs small Node apps behind Apache through Phusion Passenger. It suits small Express or Fastify APIs. A full Next.js server is a poor fit: a Next.js build can need more memory than a shared account allows (so build locally and upload the output), the app shares the account's CPU and memory limits, and nothing long-running outside the web server is allowed. If you want to try it, follow Deploy a Node.js app on shared hosting and test carefully; for production SSR, the App Platform or a VPS is the safer choice.
8. ISR: the feature that bites people
Incremental Static Regeneration is genuinely useful: pre-render at build time, regenerate on a schedule (or on-demand). But it has hosting-dependent gotchas:
On Vercel: ISR works out of the box. Revalidate triggers on the next request after the TTL expires.
On Cloudflare: ISR works through the OpenNext adapter, but it needs a cache store configured (for example R2 or KV). Read the adapter's caching docs before you rely on on-demand revalidation.
On a VPS: ISR works, but be aware:
- Revalidation writes to disk under
.next/cache/. Make sure your filesystem allows writes (it does on standard VPS, but read-only-rootfs deployments break this). - Multiple Next.js instances behind a load balancer don't share cache by default — each instance regenerates independently. Use shared cache (Redis adapter for Next.js cache) for multi-instance setups.
- The
revalidate: 60you set in code means "regenerate at most once per 60 seconds, lazily on next request". A site with no traffic doesn't regenerate.
On shared hosting with a static export: there is no ISR, because there is no Next.js server. Rebuild in CI when content changes and upload the new out/ folder.
9. A VPS with Cloudflare in front (the budget play)
For Indian-audience Next.js sites that don't justify a paid platform plan, this is often a good cost/performance combination:
- Deploy Next.js to a Domain India VPS with the systemd and Caddy setup above.
- Put Cloudflare (free plan) in front of it.
- Cloudflare's Indian edge locations serve cached pages and assets to Indian users in a few milliseconds.
- Cache misses pay the round trip to your VPS, which the user perceives as "first load slower, then fast".
For most landing pages, marketing sites and brochureware, this stack has a fixed monthly cost with no usage-based bandwidth surprises. For highly dynamic apps (real-time dashboards, checkout flows), the cache-miss penalty matters more, so measure it from India before you commit. Our Cloudflare setup guide covers the DNS, SSL and caching settings.
10. Common errors and what they actually mean
Image with src "..." has either width or height modified, but not the other. — Next/Image needs both dimensions to prevent layout shift. Either provide both width and height, or use fill mode with a sized parent.
A required parameter (id) was not provided as a string in generateStaticParams — an App Router generateStaticParams returned the wrong shape. It must return an array of plain objects such as [{ id: '1' }, { id: '2' }], with string values. The { params: { id } } wrapper belongs to the Pages Router's getStaticPaths, not the App Router.
Module not found: Can't resolve 'fs' (or 'child_process', 'path') — you're trying to import Node-only modules in code that runs on the edge / browser. Move the import inside a server component, API route, or getServerSideProps.
Error: API Resolved without sending a response — your API route handler ran but didn't call res.end(), res.json(), or res.send(). Add a return.
Error: ENOSPC: System limit for number of file watchers reached — dev mode hit Linux's inotify limit. Increase: echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p.
<--- JS stack trace ---> FATAL ERROR: ... JavaScript heap out of memory — build OOM. See "Build memory" section above.
Error: getaddrinfo EAI_AGAIN api.example.com — DNS resolution failed at runtime. Common on isolated build environments. Make sure your VPS has working DNS (/etc/resolv.conf).
Error occurred prerendering page "..." followed by network error — you're calling an API at build time that's not reachable from your build environment. Move data fetching to runtime (getServerSideProps) or pre-fetch from CI.
Error: Page "..." is missing param "..." in "generateStaticParams" — App Router dynamic route had a request for a slug not in the static-params list. Either add the slug, or set dynamicParams: true (default) and ensure the page handles SSR fallback.
TypeError: Cannot read properties of undefined (reading 'pathname') at NextRouter — old next/router used in App Router context. Migrate to next/navigation (useRouter, usePathname).
Hydration mismatch (Text content does not match server-rendered HTML) — server and client rendered different HTML. Common causes: Date.now()/Math.random() outside useEffect, browser-only globals (window, document) accessed during SSR, conditional rendering based on typeof window. Fix: gate with useEffect or useState initialisation.
11. Performance baselines: what good looks like
For a typical Next.js marketing site with ~10 pages, ~50 KB JS bundle:
| Metric | Static export (CDN) | SSR on VPS + Cloudflare | SSR on VPS direct |
|---|---|---|---|
| TTFB (Indian user) | 5-30 ms | 5-20 ms (cache hit) / origin round trip (miss) | Origin round trip (100+ ms from a distant server) |
| LCP (Largest Contentful Paint) | 0.8-1.5 s | 1.0-1.8 s | 2.0-3.5 s |
| CLS (Layout shift) | 0 | 0 | 0 |
| INP (Interaction to Next Paint) | 50-100 ms | 50-100 ms | 50-100 ms |
These are illustrative figures for a well-built site, not measurements. The biggest single win is the CDN: without one, every request pays the full round trip to your server.
Where Domain India fits
- Static Next.js sites: shared cPanel or DirectAdmin hosting with
output: 'export'. See the cPanel and DirectAdmin plan pages. - Managed Node.js hosting: the App Platform, from ₹100 a month; Node.js is auto-detected and PostgreSQL and SSL are included.
- Full control: a self-managed VPS, from ₹553 a month, with root access for systemd, Caddy or nginx, and Cloudflare in front. You manage the server and its backups; VPS plans include no snapshots or backups.
Prices are Domain India list prices, per month, excluding 18% GST. For questions about your account or plan, use 24/7 live chat or a support ticket; tickets get a first response within 15 minutes, and there is no phone support. Support can't debug your application code on a self-managed VPS.
Bottom line
For a new Next.js project starting today, try a managed platform first. If you need self-hosted infrastructure (a cost ceiling, colocation with your backend), a VPS with Cloudflare in front is a strong combination with a fixed monthly cost. For pure-static Next.js (marketing site, blog, documentation), shared hosting works well with output: 'export'.
Frequently asked questions
Why not just use Vercel for everything?
Vercel is a very good Next.js host when you want zero ops. The reasons people choose otherwise: usage costs grow with traffic, the free Hobby tier excludes commercial use, server-rendered code runs in the function region you pick rather than everywhere, and Vercel-specific features create some lock-in.
Is Cloudflare really free for production Next.js sites?
Static assets, custom domains and SSL are free, and commercial use is allowed. Server-rendered pages run as Workers, which have a free daily request allowance and a paid plan from $5 a month. Most small sites stay within the free allowance; check Cloudflare's pricing page for current limits.
Can I run Next.js on shared cPanel hosting?
A static export (output: 'export') works well on cPanel and DirectAdmin. Server-side features (API routes, SSR, middleware, ISR) need a running Next.js server; shared hosting's Node.js tool can run small Node apps, but a full Next.js server is a poor fit for the account's resource limits. For those features, use the App Platform or a VPS. See our Docker on shared hosting article for the same constraint.
What about cPanel's "Setup Node.js App"?
Setup Node.js App exists on cPanel and DirectAdmin and runs Node apps behind Apache through Phusion Passenger. It suits small Express or Fastify apps. For a full Next.js server, build locally, test carefully and expect the account's CPU and memory limits to apply; for production, the App Platform or a VPS is the safer choice.
How do I handle environment variables on a VPS?
Two patterns: (a) .env.production file at your app root (gitignored, deployed separately); (b) systemd EnvironmentFile=/etc/myapp.env directive. Don't put secrets in next.config.js — they get baked into the build output.
What's the right way to do image optimisation on a VPS?
Two options: (a) use next/image with the built-in optimiser (requires sharp to be installed: npm install sharp); (b) use a third-party image service (Cloudflare Images, Imgix, Cloudinary) and a custom loader. The first is simpler if your VPS has CPU headroom; the second is better at scale.
How do I deploy on every git push?
Use GitHub Actions or GitLab CI: build the standalone output, copy it to your VPS, and restart the systemd service. See our guide to GitHub Actions deploys to a VPS. On the App Platform, call your deploy token from CI instead.
Does Next.js work with Cloudflare's full edge stack (Workers + D1 + R2)?
Yes. The OpenNext adapter (@opennextjs/cloudflare) deploys Next.js to Cloudflare Workers, and D1 (SQLite) and R2 (object storage) are available through Workers bindings. The older @cloudflare/next-on-pages adapter is deprecated.
My Indian audience is loading the site slowly. What's the first thing to check?
Cache-hit ratio. If your Next.js app is on a VPS with Cloudflare in front, run curl -sI yoursite.com | grep cf-cache-status — if every request is MISS, your caching rules aren't right. Static assets should be HIT immediately. HTML may be DYNAMIC if it needs SSR; that's fine.
Ready to deploy? Compare VPS plans, read the App Platform guide, or open a support ticket with questions about a plan.
A self-managed Domain India VPS gives you root access to run Next.js under systemd behind Caddy or nginx, with Cloudflare in front.
See VPS plans