Client Area

Next.js Hosting in 2026 — Vercel, Cloudflare Pages, or Your Own VPS?

ByDomain India Team·DomainIndia Support
16 min readPublished 22 Apr 2026Updated 22 Jun 2026289 views

In this article

  • 1The four paths — when each is right
  • 2Latency — the part most "X vs Y" articles fudge
  • 3Path 1 — Vercel (the default)
  • 4Path 2 — Cloudflare Pages
  • 5Path 3 — Domain India VPS (self-host)

Next.js Hosting in 2026 — Vercel, Cloudflare Pages, or Your Own VPS?

Verdict at the top: For a typical small-to-medium Next.js project, the honest first answer is: don't host it yourself. Vercel's free tier or Cloudflare Pages handles 95% of Next.js workloads better than you can on a VPS, with zero operational overhead. Self-host on a Domain India VPS when (a) you've genuinely outgrown the free tiers, (b) you have data-residency constraints (DPDPA-driven hosting in India), (c) you need to keep frontend + Node backend + database on the same machine for cost, or (d) you're already running other services on the box and want to consolidate. For pure-static Next.js sites, cPanel/DirectAdmin works fine — output: 'export', upload out/ to public_html/, done. This guide covers all four paths honestly.

TL;DR
Next.js has three rendering modes (Static, SSR, ISR) and four practical hosting options (Vercel, Cloudflare Pages, Domain India VPS, Domain India shared with static export). Pick by a real decision tree, not by feeling. Vercel/Pages are the right answer for most marketing sites and content-heavy apps; VPS for control + integration with existing infrastructure; shared cPanel only for pure-static sites without API routes or middleware.

The four paths — when each is right

PathWhen to pick itMonthly 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 Pages (free / Workers Paid $5/mo)Cost-conscious; want one bill for CDN + DNS + hosting; comfortable with edge runtime constraints₹0-415
Domain India VPS (₹553/mo Starter and up)Self-host preferred; need to colocate with backend/DB; DPDPA-driven India residency; running multiple services₹553+
Domain India shared (cPanel/DA)Pure-static export only (no API routes, no SSR, no middleware)₹104/mo (cPanel Starter)

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 one of Vercel / Pages / VPS. Don't try to make it work on shared hosting.

Latency — the part most "X vs Y" articles fudge

For an Indian audience, latency from CDN edge to user matters. Rough numbers (typical Indian broadband to nearest PoP):

PathIndia RTT to first byte
Vercel (Mumbai edge enabled, Pro plan)15-30 ms
Vercel (Hobby plan, no India edge)150-250 ms (US west / Singapore route)
Cloudflare Pages (BOM PoP, native)5-15 ms
Domain India shared cPanel (Hetzner DE, no CDN)130-180 ms
Domain India VPS (Hetzner DE, no CDN)130-180 ms
Domain India VPS + Cloudflare in front (BOM PoP)5-15 ms (cached) / 130-180 ms (origin miss)

Takeaway for Indian-audience sites: Cloudflare Pages and Cloudflare-fronted VPS are tied for best Indian latency. Vercel Hobby (free) is significantly worse for India because Mumbai edge is Pro-tier only. If your audience is India-heavy and you want to use Vercel, factor in the Pro upgrade.

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: instant deploys from git, first-class Next.js features (every Next.js feature works because Vercel made the framework), built-in image optimisation, edge functions, free SSL, automatic preview deploys per PR, free analytics.

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.
  • No India edge on Hobby. Mumbai edge region is Pro-only. Free-tier Indian users get routed via Singapore or US West.
  • Bandwidth pricing is the gotcha. Pro includes 1 TB/month; overage is $0.15/GB. A medium-traffic site can hit thousands of dollars/year if it goes viral.
  • 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:

bash
npm install -g vercel
cd my-next-app
vercel                 # interactive setup
vercel --prod          # deploy production

That's it. Push to git → auto-deploy. Custom domain via Vercel's dashboard.

When to graduate off Vercel: when bandwidth bills exceed ~$200/month, when you need data residency in India (DPDPA), when you want to colocate with your own backend services, or when ops capacity to self-host is no longer the bottleneck.

Path 2 — Cloudflare Pages

Best when: you want low-cost India-edge hosting and your Next.js app fits within Cloudflare's runtime constraints.

The good: Cloudflare's full India PoP coverage (BOM, MAA, DEL, BLR, HYD, CCU); free tier includes unlimited bandwidth, unlimited requests, build minutes, custom domains; tight integration with Workers, D1, R2, KV.

The catches:

  • Edge runtime, not Node.js runtime. Many npm packages assume Node-specific APIs (fs, full Buffer, native modules) — these don't work. Common packages that break: image-processing libs (sharp), some ORMs, anything using child_process.
  • Less battle-tested for full Next.js feature set than Vercel. ISR, complex SSR with streaming, App Router edge cases sometimes need workarounds.
  • Build limits on free tier: 500 builds/month, 20 minutes per build. Plenty for most projects.
  • Bandwidth to your own origin, when needed, costs nothing — uniquely good story.

Setup: connect Pages to your GitHub repo, choose Next.js framework preset, deploy. Auto-deploy on push.

For Next.js apps that primarily use SSG with a few API routes, Cloudflare Pages is excellent and free. For apps using complex middleware, image optimisation, or non-edge-compatible packages, Vercel is the safer bet.

Path 3 — Domain India VPS (self-host)

Best when: you want infrastructure control, need data residency in India (or in EU under Hetzner), have multiple services to colocate, or want a fixed monthly cost regardless of traffic.

Build for production

Configure next.config.js for standalone output:

javascript
/** @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:

bash
npm install --legacy-peer-deps     # only if you have peer-dep conflicts
npm run build

Standalone output structure:

.next/standalone/server.js        ← the entry point
.next/standalone/.next/...
.next/standalone/package.json
.next/static/                     ← copy alongside
public/                           ← copy alongside

Deploy package — what to ship

bash
# 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:

ini
[Unit]
Description=My Next.js App
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/myapp
Environment=NODE_ENV=production
Environment=PORT=3000
Environment=HOSTNAME=0.0.0.0
ExecStart=/usr/bin/node server.js
Restart=on-failure
User=myapp
Group=myapp

[Install]
WantedBy=multi-user.target
bash
sudo systemctl enable --now myapp
sudo systemctl status myapp
sudo journalctl -u myapp -f       # tail logs

systemd 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:

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

server {
    listen 443 ssl http2;
    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";
    }
}
bash
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Or skip nginx and use Caddy, which auto-handles SSL via Let's Encrypt:

caddy
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 VPS Starter (2 GB RAM), a medium-sized Next.js app can OOM during npm run build. Symptoms:

<--- Last few GCs --->
[12345:0x...] FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory

Two fixes:

  1. Build elsewhere, deploy artefacts. Run npm run build in CI (GitHub Actions) or on your laptop, then rsync the standalone output. Most-recommended pattern.
  2. 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

```

  1. Bump Node's heap limit (less reliable):

```bash

NODE_OPTIONS='--max-old-space-size=2048' npm run build

```

If your app build needs > 4 GB on a VPS Starter, upgrade to VPS Basic (4 GB RAM) — or build in CI.

Path 4 — Static export to Domain India shared cPanel/DirectAdmin

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.

javascript
// 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;
bash
npm run build
# Creates out/ directory with static HTML/CSS/JS

Upload out/ contents to public_html/ via cPanel File Manager, FTP, or GitHub Actions → cPanel.

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 (revalidate in getStaticProps) — 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.

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 Pages: ISR support is partial. Time-based revalidate works; on-demand revalidate via revalidatePath requires Workers + D1 setup.

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: 60 you 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 cPanel/DA: ISR doesn't work — there's no Node runtime. Use output: 'export' and forget about ISR; rebuild via CI when content changes.

Domain India + Cloudflare in front (the budget-friendly Indian-latency play)

For Indian-audience Next.js sites that don't justify Vercel Pro, this is often the best cost/performance combination:

  1. Deploy Next.js to Domain India VPS Starter (₹553/month) via the systemd + Caddy setup above.
  2. Front it with Cloudflare (free plan).
  3. Cloudflare's BOM/MAA PoPs serve cached pages and assets at 5-15 ms RTT to Indian users.
  4. Cache misses go to your VPS at 130-180 ms (Hetzner DE), which the user perceives as "first slow load, then fast".

For most Indian SaaS landing pages, marketing sites, and brochureware, this stack is significantly cheaper than Vercel Pro and faster than Vercel Hobby (because Vercel Hobby doesn't have Mumbai edge).

For high-dynamism apps (real-time dashboards, e-commerce checkout flows), the cache-miss penalty hurts more — at that point, Vercel Pro's Mumbai edge or hosting closer to your audience is worth the upgrade.

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 (params) was not provided as a string in generateStaticParams` — App Router dynamic route's generateStaticParams returned undefined or wrong shape. Must return { params: { id: 'string' } } shape, not { id: 'string' }.

`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.

Performance baselines — what good looks like

For a typical Next.js marketing site with ~10 pages, ~50 KB JS bundle:

MetricStatic export (CDN)SSR on VPS + CloudflareSSR on VPS direct
TTFB (Indian user)5-30 ms5-20 ms (cache hit) / 130-180 ms (miss)130-180 ms
LCP (Largest Contentful Paint)0.8-1.5 s1.0-1.8 s2.0-3.5 s
CLS (Layout shift)000
FID/INP50-100 ms50-100 ms50-100 ms

The biggest single win is the CDN. Without a CDN, even on a VPS, Indian users see 130+ ms TTFB which Google's Core Web Vitals will flag.

Frequently asked questions

Q Why not just use Vercel for everything?

Vercel is genuinely the best Next.js host for projects where you want zero ops. The reasons people don't: bandwidth costs scale poorly (1 TB included, $0.15/GB after), Hobby tier excludes commercial use, India edge requires Pro, and vendor lock-in via Vercel-specific features. For projects where these matter, alternative paths beat Vercel.

Q Is Cloudflare Pages really free for production sites?

Yes — unlimited bandwidth, unlimited requests, custom domains, SSL all on the free tier. Build minutes (500/month) and Workers requests (100k/day) are the practical limits. Most small sites never hit them.

Q Can I run Next.js on shared cPanel hosting?

Only as a static export (output: 'export'). Server-side features (API routes, SSR, middleware, ISR) need a Node runtime, which shared hosting can't provide due to long-running-process restrictions. See our Docker on cPanel and Laravel on cPanel articles for the same constraint.

Q What about cPanel's "Setup Node.js App"?

It works for basic Express apps but is unreliable for full Next.js. Phusion Passenger (which cPanel uses) doesn't handle Next.js's standalone output cleanly. We've seen customers waste days fighting it. If you need Node in production, get a VPS.

Q Should I use Pages Router or App Router in 2026?

App Router is the default for new projects. Pages Router is still supported and used by many existing apps. App Router has better RSC (React Server Components) story, server actions, and metadata API; Pages Router has better library compatibility for older third-party packages. Migrate when you have time, not as an emergency.

Q 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.

Q 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.

Q How do I deploy on every git push?

GitHub Actions or GitLab CI. Build the standalone output, rsync to your VPS, restart systemd. We have a separate guide for GitHub Actions deploy to VPS.

Q Vercel vs Domain India VPS for production?

Vercel: zero ops, scales automatically, expensive at high traffic. VPS: predictable cost, full control, requires you to handle scaling, deploys, monitoring. For a small team without ops capacity, Vercel; for cost-conscious or DPDPA-driven, VPS.

Q Does Next.js work with Cloudflare's full edge stack (Workers + D1 + R2)?

Yes. The @cloudflare/next-on-pages adapter lets you deploy Next.js to Cloudflare Pages with edge runtime. D1 (SQLite) and R2 (object storage) work via Workers bindings. Use this when you want a fully edge-native, low-latency, low-cost stack.

Q 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.

Bottom line

For a new Next.js project starting today: try Vercel free first. If you hit pricing limits or India-latency issues on the Hobby tier, evaluate Cloudflare Pages — it's free for unlimited traffic and has India PoP coverage out of the box.

If you need self-hosted infrastructure (cost ceiling, data residency, colocation with backend), Domain India VPS Starter at ₹553/month + Cloudflare in front is a strong combination — production-ready Next.js for less than ₹1,000/month including the CDN, with no usage-based bandwidth surprises.

For pure-static Next.js (marketing site, blog, documentation), our shared cPanel/DirectAdmin works fine with output: 'export'. Don't try to make API routes / SSR / middleware work on shared — they require a Node runtime that shared hosting deliberately doesn't provide.

If you want help setting up the systemd + Caddy + Cloudflare stack for a Next.js app on Domain India VPS, [email protected] — we troubleshoot Next.js production setups as part of standard VPS support.

Self-hosting Next.js? Domain India VPS Starter ₹553/month — root access, Caddy auto-SSL, sized for production Next.js with Cloudflare in front. Get a VPS plan

Was this article helpful?

Your feedback helps us improve our documentation

Still need help? Submit a support ticket