Loading theme
~/saverio

Troubleshooting dei deploy

9 feb 2025· agg. 24 feb 2026· 8 min
troubleshootingnextjsprismabetter-authcoolify

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:

  1. Aggiungi uno script prebuild nel package.json dell'app:
{
  "scripts": {
    "prebuild": "prisma generate",
    "build": "next build"
  }
}
  1. In prisma.config.ts, aggiungi un fallback per quando DATABASE_URL manca 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: &copy; {new Date().getFullYear()}
const COPYRIGHT_YEAR = process.env.NEXT_PUBLIC_COPYRIGHT_YEAR ?? "2026";
// &copy; {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:

  1. Imposta le env var in Coolify: NEXT_PUBLIC_SITE_URL e BETTER_AUTH_URL (entrambi a https://your-domain.com). NEXT_PUBLIC_* è embeddato al build time; assicurati che parta un redeploy dopo averla aggiunta.
  2. Fallback nel codice: usa BETTER_AUTH_URL ?? NEXT_PUBLIC_SITE_URL ?? "http://localhost:3000" per baseURL così almeno una viene usata.
  3. 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.
  4. 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 di 5432 (PostgreSQL). I port mapping tipo 3000:5432 servono per accesso esterno; il traffico interno container-to-container usa 5432.
  • Mismatch della modalità SSL (es. sslmode=verify-full con un Postgres interno senza certificato)

Fix:

  1. In Coolify, usa il Postgres URL (internal) dalla resource PostgreSQL. Sostituisci /postgres con /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.
  2. La porta deve essere 5432. Esempio: postgresql://user:pass@awksg4ssww4ooscwwc8gks8g:5432/mazzaimmobiliare?sslmode=disable
  3. Per il Postgres Docker interno con SSL disabilitato, usa sslmode=disable.
  4. 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:

  1. Abilita Public Development URL in Cloudflare R2: bucket → Settings → Public access → abilita "Public Development URL". Copia l'URL (es. https://pub-xxxxx.r2.dev).
  2. Imposta R2_PUBLIC_URL in Coolify all'URL pubblico (senza virgolette):
    • R2.dev: https://pub-xxxxx.r2.dev (senza path, senza /mazzaimmobiliare)
    • Dominio custom: https://images.mazzaimmobiliare.it
  3. Fixa gli URL nel DB se puntano a r2.cloudflarestorage.com: esegui pnpm fix:r2-urls (con tunnel SSH se il DB è remoto).
  4. Assicurati che next.config.ts images.remotePatterns includa il tuo dominio (images.mazzaimmobiliare.it o *.r2.dev).
  5. 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:

  1. Esponi PostgreSQL sull'host: in Coolify, apri la resource PostgreSQL → ConfigurationPorts (o Advanced). Aggiungi il mapping 5433:5432 (host 5433 → container 5432). Salva e Restart la resource.

  2. Verifica sul server:

    ss -tlnp | grep 5433
    

    Devi vedere qualcosa in ascolto sulla porta 5433.

  3. 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
    
  4. .env locale: usa localhost:5433 per il tunnel:

    DATABASE_URL="postgresql://postgres:PASSWORD@localhost:5433/your_db_name?sslmode=disable"
    

    Sostituisci PASSWORD e your_db_name coi valori da Coolify.

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


ProblemaFix
Prisma + Node 22 ESMNODE_OPTIONS='--experimental-require-module' in nixpacks.toml
Prisma generate fallisceprebuild: "prisma generate"; fallback datasource in prisma.config.ts quando DATABASE_URL manca
Errori Cache ComponentsDisabilita cacheComponents e usa dynamic = "force-dynamic" sulle route DB/auth
new Date() nel prerenderUsa anno statico o env var
next start + standaloneRimuovi output: "standalone"; la COPY finale di Nixpacks sovrascrive l'output di build
Better Auth Invalid originImposta NEXT_PUBLIC_SITE_URL e BETTER_AUTH_URL; usa disableOriginCheck: true come workaround
Prisma P1008 / EAI_AGAINUsa il Postgres URL (internal), host spesso UUID da Coolify, porta 5432, sslmode=disable
Le immagini non si vedonoR2 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 / 404Il record A del dominio root deve puntare all'IP del server Coolify, non al vecchio hosting
Accesso Prisma Studio / DBAggiungi port mapping 5433:5432 nel Postgres Coolify; SSH tunnel -L 5433:localhost:5433; usa localhost:5433 nel DATABASE_URL locale