Troubleshooting dei deploy
Questa sezione copre i fallimenti comuni di build e runtime con Next.js 16, Prisma 7 e Nixpacks su Coolify.
Versioni stack assunte: Coolify con Nixpacks come build pack, monorepo con pnpm (Base Directory = /), Next.js 16, Prisma 7, Node 22 (default Nix).
1. Prisma + Node 22: ERR_REQUIRE_ESM
Errore: require() of ES Module ... zeptomatch ... not supported
Causa: il dev tooling di Prisma 7 usa zeptomatch (solo ESM). Il Node 22.11 di Nix non abilita require(esm) di default.
Fix: aggiungi a nixpacks.toml alla root della repo:
[variables]
NODE_OPTIONS = '--experimental-require-module'
Imposta anche COREPACK_INTEGRITY_KEYS = '' se corepack fallisce con verifica della firma.
2. Prisma generate prima del build
Errore: prisma o PrismaClient non trovato durante next build, oppure il build fallisce durante prisma generate.
Causa: (a) pnpm install --ignore-scripts (usato da Nixpacks) salta postinstall, quindi prisma generate non parte mai. (b) Prisma 7 richiede DATABASE_URL in prisma.config.ts anche per generate (che non si connette al DB); se manca al build time, la config non si carica.
Fix:
- Aggiungi uno script
prebuildnelpackage.jsondell'app:
{
"scripts": {
"prebuild": "prisma generate",
"build": "next build"
}
}
- In
prisma.config.ts, aggiungi un fallback per quandoDATABASE_URLmanca al build time:
datasource: {
url: process.env["DATABASE_URL"] ?? "postgresql://localhost:5432/dummy",
},
prisma generate legge solo lo schema e non si connette al database; l'URL dummy viene usato solo quando la env var è mancante. A runtime, l'app usa il DATABASE_URL reale.
3. Cache Components: dati non cacheati fuori da Suspense
Errore: Uncached data was accessed outside of <Suspense>
Causa: Next.js 16 con cacheComponents: true (Partial Prerendering) richiede che tutti i dati dinamici (DB, auth, new Date()) stiano dentro boundary <Suspense>. Le pagine che usano Prisma o Clerk direttamente al top level falliscono il prerender.
Opzioni:
A. Disabilita Cache Components (più semplice): rimuovi cacheComponents: true da next.config.ts. Usa export const dynamic = "force-dynamic" sulle route che hanno bisogno di DB o auth:
// pagine che usano Prisma, Clerk o dati request-time
export const dynamic = "force-dynamic";
export default async function Page() { ... }
B. Adotta Cache Components: avvolgi ogni route in un wrapper sync con <Suspense> intorno a un async child che chiama connection() prima di Prisma. Più lavoro, meglio per lo streaming.
4. new Date() nei Server Component
Errore: Route "/" used new Date() before accessing ... uncached data or Request data
Causa: con Cache Components, new Date() è non deterministico e blocca il prerender a meno che il componente non abbia già chiamato connection(), cookies() o simili.
Fix: usa un anno statico o una env var:
// Invece di: © {new Date().getFullYear()}
const COPYRIGHT_YEAR = process.env.NEXT_PUBLIC_COPYRIGHT_YEAR ?? "2026";
// © {COPYRIGHT_YEAR}
5. output: "standalone" vs next start
Warning: "next start" does not work with "output: standalone" oppure Cannot find module '.next/standalone/server.js'
Causa: con output: "standalone", Next.js crea una folder .next/standalone/ minimale con server.js. Coolify/Nixpacks usa pnpm --filter blog start, che esegue next start di default. Se cambi lo script di start in node .next/standalone/server.js, il build può comunque fallire: Nixpacks fa una COPY . /app finale che sovrascrive il build context, quindi .next/standalone (creata durante il build) non è presente nell'immagine finale.
Fix: rimuovi output: "standalone" da next.config.ts e usa next start. L'immagine sarà più grande ma funziona in modo affidabile con Nixpacks.
6. Better Auth: Invalid origin
Errore: ERROR [Better Auth]: Invalid origin: https://your-domain.com
Causa: Better Auth valida l'header Origin contro trustedOrigins. Un bug in Better Auth 1.4.x può rifiutare origin validi anche quando sono in lista. Inoltre, se baseURL non è impostato (o se BETTER_AUTH_URL / NEXT_PUBLIC_SITE_URL mancano a runtime), getTrustedOrigins può restituire un array vuoto.
Fix:
- Imposta le env var in Coolify:
NEXT_PUBLIC_SITE_URLeBETTER_AUTH_URL(entrambi ahttps://your-domain.com).NEXT_PUBLIC_*è embeddato al build time; assicurati che parta un redeploy dopo averla aggiunta. - Fallback nel codice: usa
BETTER_AUTH_URL ?? NEXT_PUBLIC_SITE_URL ?? "http://localhost:3000"perbaseURLcosì almeno una viene usata. - Workaround: se l'errore persiste, aggiungi
advanced: { disableOriginCheck: true }alla config di Better Auth. Disabilita la validazione dell'origin (e CSRF per backward compatibility). Riabilitala quando Better Auth fixa il bug di trustedOrigins. - Opzionale: usa la env var
BETTER_AUTH_TRUSTED_ORIGINS(separata da virgole) per aggiungere origin extra.
7. Prisma: Operation has timed out (P1008) o EAI_AGAIN
Errore: Invalid prisma.town.findUnique() invocation: Operation has timed out (P1008) oppure getaddrinfo EAI_AGAIN
Causa: il container dell'app non riesce a raggiungere PostgreSQL, oppure la connessione si pianta. Errori comuni in Coolify:
- Host sbagliato: uso dell'IP esterno del server (es.
5.75.134.45) invece del nome del container Postgres. L'app gira dentro Docker e deve usare l'hostname interno. - Porta sbagliata: uso della porta
3000(Next.js) invece di5432(PostgreSQL). I port mapping tipo3000:5432servono per accesso esterno; il traffico interno container-to-container usa5432. - Mismatch della modalità SSL (es.
sslmode=verify-fullcon un Postgres interno senza certificato)
Fix:
- In Coolify, usa il Postgres URL (internal) dalla resource PostgreSQL. Sostituisci
/postgrescon/mazzaimmobiliare(o il nome del tuo DB). L'host è spesso un UUID (es.awksg4ssww4ooscwwc8gks8g), non il nome della resource; copia l'URL esatto da Coolify. - La porta deve essere 5432. Esempio:
postgresql://user:pass@awksg4ssww4ooscwwc8gks8g:5432/mazzaimmobiliare?sslmode=disable - Per il Postgres Docker interno con SSL disabilitato, usa
sslmode=disable. - Assicurati che l'app sia linkata alla resource PostgreSQL in Coolify (stesso project/network).
8. Le immagini non si vedono
Causa: R2_PUBLIC_URL sbagliato o mancante, accesso pubblico al bucket R2 disabilitato, gli URL delle immagini nel DB puntano al dominio sbagliato (es. S3 API), oppure bug di image optimization di Next.js 16 ("url parameter is not allowed" per domini remoti).
Importante: le immagini sono servite usando l'url salvato nel database. R2_PUBLIC_URL impatta solo nuovi upload e lo script di fix; non cambia i record DB esistenti.
Errore comune: gli URL nel DB puntano all'endpoint S3 API:
https://xxxxx.r2.cloudflarestorage.com/... ❌
Quell'URL serve per le operazioni S3 API (upload/delete), non per servire immagini ai browser.
Fix:
- Abilita Public Development URL in Cloudflare R2: bucket → Settings → Public access → abilita "Public Development URL". Copia l'URL (es.
https://pub-xxxxx.r2.dev). - Imposta
R2_PUBLIC_URLin Coolify all'URL pubblico (senza virgolette):- R2.dev:
https://pub-xxxxx.r2.dev(senza path, senza/mazzaimmobiliare) - Dominio custom:
https://images.mazzaimmobiliare.it
- R2.dev:
- Fixa gli URL nel DB se puntano a
r2.cloudflarestorage.com: eseguipnpm fix:r2-urls(con tunnel SSH se il DB è remoto). - Assicurati che
next.config.tsimages.remotePatternsincluda il tuo dominio (images.mazzaimmobiliare.ito*.r2.dev). - Bug remote image di Next.js 16: se le immagini falliscono nonostante un setup R2 corretto, Next.js 16 può bloccare certi domini remoti nell'optimizer. Usa un loader custom che restituisce gli URL esterni così come sono (bypassando l'ottimizzazione):
// image-loader.ts
export default function customImageLoader({ src, width, quality }: { src: string; width: number; quality?: number }) {
if (src.startsWith("http://") || src.startsWith("https://")) return src;
return `/_next/image?url=${encodeURIComponent(src)}&w=${width}&q=${quality ?? 75}`;
}
In next.config.ts: images: { loader: "custom", loaderFile: "./image-loader.ts", ... }.
9. No available server o 404
Causa: il record A del dominio root punta all'IP sbagliato (es. il vecchio hosting invece che Coolify).
Fix: in Cloudflare (o sul tuo provider DNS) → DNS → trova il record A per @ o per il tuo dominio. Deve puntare all'IP del tuo server Coolify/Hetzner, non all'IP del vecchio hosting (es. 216.150.1.1). Aggiornalo all'IP giusto e aspetta la propagazione.
10. Prisma Studio / client DB via SSH tunnel
Obiettivo: collegare Prisma Studio, prisma migrate o qualunque client DB al PostgreSQL remoto su Hetzner.
Causa: Coolify tiene PostgreSQL interno di default (nessun port mapping verso l'host). Niente è in ascolto sul localhost:5432 o localhost:5433 del server, quindi un SSH tunnel non riesce a raggiungerlo.
Fix:
-
Esponi PostgreSQL sull'host: in Coolify, apri la resource PostgreSQL → Configuration → Ports (o Advanced). Aggiungi il mapping
5433:5432(host 5433 → container 5432). Salva e Restart la resource. -
Verifica sul server:
ss -tlnp | grep 5433Devi vedere qualcosa in ascolto sulla porta 5433.
-
Avvia l'SSH tunnel (lascia questo terminale aperto):
ssh -L 5433:localhost:5433 -p 2222 -o IdentitiesOnly=yes \ -o ServerAliveInterval=30 -o ServerAliveCountMax=3 \ -i ~/.ssh/hetzner-platform deploy@YOUR_SERVER_IP -
.envlocale: usalocalhost:5433per il tunnel:DATABASE_URL="postgresql://postgres:PASSWORD@localhost:5433/your_db_name?sslmode=disable"Sostituisci
PASSWORDeyour_db_namecoi valori da Coolify. -
Esegui Prisma Studio (o le migrazioni):
cd apps/blog && npx prisma studio
Conflitto sul nome container: se Coolify mostra "container name already in use" al riavvio di Postgres, rimuovi il container in conflitto sul server:
docker rm -f CONTAINER_NAME
I dati vivono nel volume, non nel container. Poi riprova Restart in Coolify.
Riepilogo
| Problema | Fix |
|---|---|
| Prisma + Node 22 ESM | NODE_OPTIONS='--experimental-require-module' in nixpacks.toml |
| Prisma generate fallisce | prebuild: "prisma generate"; fallback datasource in prisma.config.ts quando DATABASE_URL manca |
| Errori Cache Components | Disabilita cacheComponents e usa dynamic = "force-dynamic" sulle route DB/auth |
new Date() nel prerender | Usa anno statico o env var |
next start + standalone | Rimuovi output: "standalone"; la COPY finale di Nixpacks sovrascrive l'output di build |
| Better Auth Invalid origin | Imposta NEXT_PUBLIC_SITE_URL e BETTER_AUTH_URL; usa disableOriginCheck: true come workaround |
| Prisma P1008 / EAI_AGAIN | Usa il Postgres URL (internal), host spesso UUID da Coolify, porta 5432, sslmode=disable |
| Le immagini non si vedono | R2 Public URL + abilita il bucket; pnpm fix:r2-urls se il DB ha URL sbagliati; loader custom se Next.js 16 blocca i domini remoti |
| No available server / 404 | Il record A del dominio root deve puntare all'IP del server Coolify, non al vecchio hosting |
| Accesso Prisma Studio / DB | Aggiungi port mapping 5433:5432 nel Postgres Coolify; SSH tunnel -L 5433:localhost:5433; usa localhost:5433 nel DATABASE_URL locale |