Le principe à retenir
Deux apps, deux moments de lecture de l’env différents. C’est la source de 90 % des « ça marche en local mais plante en prod ».Les deux valident via
@t3-oss/env-core : une var required manquante ou malformée fait crasher au démarrage avec ❌ Invalid environment variables.API — env lue au runtime
Le bundledist/server.js lit Bun.env au démarrage. Les valeurs doivent donc être injectées dans l’environnement runtime du conteneur (docker run -e …, ou l’onglet Environment de Dokploy).
Apps Vite — env inlinée au build
Vite remplaceimport.meta.env.VITE_OPEN_URL par sa valeur littérale pendant vite build. Conséquence :
- Fournir une
VITE_*en env runtime sur Dokploy → inutile, le client ne lit jamais l’env au runtime. - Il faut la passer en build arg, et que le Dockerfile déclare le
ARGcorrespondant pour la capter.
Pièges Docker communs
Guillemets dans le .env
docker run --env-file (et Dokploy) ne strippent pas les guillemets, contrairement à Bun/dotenv en local. DATABASE_URL="postgres://…" devient littéralement "postgres://…" → Invalid URL.
ENV ne traverse pas les stages
Chaque FROM … AS … repart d’un environnement vierge. Un ENV NODE_ENV=production dans le stage installer n’existe pas dans le stage runner. À reposer dans le stage final.
Transports pino en worker thread
pino-pretty (dev) et pino-opentelemetry-transport (POSTHOG_LOGS_ENABLED=true) tournent dans un worker thread qui charge son module depuis node_modules — absent des images standalone qui ne copient que dist/. Résultat : ModuleNotFound: thread-stream, crash.
Collision de casse (macOS ↔ Linux)
macOS est insensible à la casse, Linux (donc Docker/Dokploy) sensible. Un import./Experience qui résout un fichier experience.tsx en local casse au build Docker. Toujours faire matcher exactement la casse import ↔ fichier.
Checklist Dokploy
1
API
Toutes les vars en Environment (runtime). Aucune en build arg.
NODE_ENV=production, POSTHOG_LOGS_ENABLED=false, valeurs sans guillemets.2
Apps Vite
Les
VITE_* en Build Arguments (pas runtime). Vérifier que chaque var a son ARG dans le Dockerfile de l’app.3
Reproduire en local