How to Deploy a Next.js App on European Infrastructure Without Vercel (2026)
By Michael Michelsen
Next.js is the default React framework for SaaS teams, and Vercel has been the default place to host it. That pairing is no longer a given. In 2026 an increasing number of European teams are moving Next.js off Vercel and onto European PaaS — mostly for pricing reasons, partly for data-residency reasons, and occasionally because their customers asked them to.
This is a hands-on walkthrough for deploying Next.js 15 on European infrastructure with zero cold starts, full ISR support, streaming, and proper preview environments. The examples use HostStack, but most of the patterns transfer to any EU-hosted PaaS.
Why teams move Next.js off Vercel
It is rarely one thing. The usual pattern is three overlapping reasons:
- Pricing drift. Vercel is generous on the Hobby tier, but Pro adds up once you have a team, a few preview environments, and a moderate amount of bandwidth. Image optimization charges alone can be painful for content-heavy sites.
- Data residency. Vercel is a US company. For EU customers who need a DPA with an EU controller, that is a hard stop.
- The opacity of "just push to Vercel." When something breaks — an ISR cache that will not invalidate, a middleware edge case — there is no Docker image to inspect. A self-hosted Next.js is less magical but much easier to debug.
The Next.js self-hosting story in 2026
Next.js 13 introduced the App Router and made self-hosting genuinely painful for a while. Next.js 14 and 15 reversed that — next build now produces a standalone output that runs as a single Node.js process, with server components, streaming, ISR, middleware, and image optimization all working out of the box.
Three things are true of Next.js self-hosting today:
- The Dockerfile is fifteen lines.
- ISR works with any filesystem that persists across restarts.
- Middleware and Edge Runtime work on a regular Node server; the "edge" distinction is mostly a Vercel-specific deployment detail.
Step 1: Configure standalone output
In next.config.js, enable standalone output. This tells next build to emit a self-contained directory with only the files needed to run the server.
// next.config.js
module.exports = {
output: 'standalone',
images: {
// Use the built-in Sharp optimizer, not the Vercel image API.
formats: ['image/avif', 'image/webp'],
},
experimental: {
// Keep ISR revalidations working across restarts.
isrMemoryCacheSize: 0,
},
};Step 2: Write a boring Dockerfile
You do not need a clever multi-stage Dockerfile. The Next.js docs publish one that works; here is a pared-down version optimized for a European PaaS:
# syntax=docker/dockerfile:1.6
FROM node:22-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --frozen-lockfile
FROM node:22-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV PORT=3000
RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]HostStack auto-detects Next.js and will write this Dockerfile for you if you do not have one. You can accept the default or drop in your own.
Step 3: Handle ISR with a persistent disk
Incremental Static Regeneration writes pages to .next/cache. If the container restarts, you lose the cache unless the directory is on a persistent volume.
On HostStack, add a persistent disk mounted at /app/.next/cache and give it 1–10 GB depending on how many pages you generate. On other platforms the mechanism is the same; the flag might be called a "volume" or "persistent storage."
If you run multiple replicas, swap the filesystem cache for a Redis-backed cache handler. Next.js 15 added a pluggable cache API; here is the pattern:
// cache-handler.js
const { createClient } = require('redis');
const client = createClient({ url: process.env.REDIS_URL });
client.connect();
module.exports = class CacheHandler {
async get(key) {
const v = await client.get(`next:${key}`);
return v ? JSON.parse(v) : null;
}
async set(key, data, ctx) {
await client.set(`next:${key}`, JSON.stringify({ value: data, lastModified: Date.now(), tags: ctx?.tags ?? [] }));
}
async revalidateTag(tag) {
// Minimal implementation; production code should track tag → key mapping
// in a Redis set instead of scanning.
}
};Step 4: Wire environment variables
Next.js distinguishes between server and build-time variables. On Vercel this is handled invisibly; self-hosted you have to be deliberate:
NEXT_PUBLIC_*variables are baked into the client bundle at build time. On HostStack, mark them as build-time env vars so they are injected beforenext buildruns.- Server-only secrets (
DATABASE_URL,STRIPE_SECRET_KEY) should be runtime-only and marked as secrets so they are encrypted at rest and never logged. NEXTAUTH_SECRET,AUTH_SECRETneed to be consistent across replicas. Generate once withopenssl rand -base64 32and pin it.
Step 5: Give it a managed Postgres
Self-hosted Next.js usually means self-hosted Prisma or Drizzle too. Provision a managed PostgreSQL in the same EU region as your application — Nuremberg or Helsinki on HostStack, Frankfurt or Dublin on most hyperscalers.
Keep the database in the same region as the app, and connect via an internal network (DATABASE_URL pointing at a private DNS name). Internal traffic is free on HostStack; on other platforms it is often free too, but check the bill.
Run migrations as a one-off before the rollout:
# In hoststack.yaml or your platform equivalent
predeploy:
- npx prisma migrate deployStep 6: Preview environments on every PR
One of Vercel's strongest features is a live URL for every pull request. Replicating that on a European PaaS is straightforward if you pick a platform that supports it natively. HostStack spins up a preview environment per pull request with an isolated database and its own URL; teardown is automatic on merge.
Three rules to keep previews useful:
- Seed them with a realistic but anonymized copy of production data. Do not copy personal data into a preview environment — that is a GDPR transfer you did not intend.
- Use a shorter-lived database (reset on branch close) unless you need multi-day QA windows.
- Gate access with HostStack's IP allowlist or a simple basic-auth middleware so preview URLs are not publicly crawlable.
Step 7: Images, fonts, and the edge
Vercel's next/image defaults to their image optimization endpoint. Self-hosted, next/image falls back to Sharp, which runs inside your container. For moderate traffic that is fine; for heavy image-processing workloads, offload to a purpose-built service (imgproxy, imgix, or a Cloudflare Worker in front of an S3 bucket).
Fonts loaded with next/font are downloaded at build time and inlined — no changes needed. If you were relying on Vercel's edge caching for cold-path assets, put Cloudflare or Fastly in front of your HostStack URL. They play well together.
What you gain, what you give up
Moving Next.js off Vercel onto a European PaaS is the right call for a lot of teams, but not all of them. The trade-offs:
- You gain: predictable invoices, a single EU DPA, Docker-level debuggability, and full control over caching and middleware behavior.
- You give up: Vercel's polished previews (most EU platforms now match this), their global edge network (meaningful if your app is read-heavy and globally distributed), and their image optimization at Vercel scale.
For 80% of SaaS Next.js apps, those trade-offs favor self-hosting. If your app is a marketing site with global traffic and no login, stay on Vercel. If your app is a real product with European customers, the cost and compliance math tilts the other way.
Bottom line
Next.js self-hosting is not a lost art. In 2026 it is a fifteen-line Dockerfile, a persistent disk, a managed Postgres, and a hoststack.yaml.
If you want the shortest path to trying it, open a free HostStack account, point it at your Next.js repository, and let the framework auto-detection do the first deploy. The second deploy is a git push. The third is muscle memory.
Found this useful? Create a free HostStack account and deploy on European infrastructure in about a minute — no credit card required.