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. If you are still deciding whether to leave Vercel at all, the HostStack vs Vercel comparison lays out prices and the cases where Vercel stays the better buy; if you have decided, the Vercel migration guide has the cutover order.
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 (Vercel Inc., Delaware). Its DPA covers Pro and Enterprise only and relies on Standard Contractual Clauses for EU transfers — a harder procurement conversation than an EU-incorporated provider.
- The opacity of "just push to Vercel." When something breaks — an ISR cache that will not invalidate, a middleware edge case — the default Vercel build gives you no 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.
module.exports = {
output: 'standalone',
images: {
// Use the built-in Sharp optimizer, not the Vercel image API.
formats: ['image/avif', 'image/webp'],
},
// No per-process ISR memory cache: every replica reads the same
// filesystem (or Redis) cache, so revalidations survive restarts.
cacheMaxMemorySize: 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
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 fills in framework defaults for any build or start command you leave empty, so you can skip the Dockerfile entirely or drop in your own — Deploying covers both paths, and the Next.js deploy guide lists exactly which commands the detection fills in.
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 volume 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:
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, so they must reachnext build. On HostStack the default scope (Build + Runtime) does that; with your own Dockerfile, declare each one withARG. One trap:hoststack env importstores imported variables as runtime-only secrets unless told otherwise, which keeps them out of the build. The environment variables docs list the scopes.- Server-only secrets (
DATABASE_URL,STRIPE_SECRET_KEY) should be runtime-only secrets, so they are masked in the dashboard and CLI and never handed to the build. 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 — Helsinki (Finland) 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 pre-deploy command. It runs inside the new container after it passes its health check and before it takes traffic; if it fails, the deploy fails and the previous version keeps serving. With the HostStack CLI:
hoststack services update svc_... --pre-deploy-command "npx prisma migrate deploy"The same setting is preDeployCommand in the TypeScript SDK, if you script your infrastructure.
Step 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 deploys a preview environment per pull request with its own URL, and removes it when the PR closes. A preview connects to the databases its service is linked to and runs the same pre-deploy command, so build previews from a staging service linked to a staging database, or a PR's migration reaches production.
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.
- Reset the staging database between QA cycles rather than letting it drift, unless you need multi-day QA windows.
- Gate access with 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.