Skip to main content

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 bundle dist/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).
Ne pas déclarer ces vars en ARG dans le Dockerfile. En voyant un ARG DATABASE_URL, Dokploy les classe en build-args et ne les injecte plus au runtime → undefined au boot. L’API n’a besoin d’aucune var au build (runtimeEnv: Bun.env est lu au runtime).

Apps Vite — env inlinée au build

Vite remplace import.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 ARG correspondant pour la capter.
Ajouter une VITE_* au schéma env.ts sans ajouter son ARG au Dockerfile → Vite l’inline à undefined → crash runtime. Les deux vont toujours ensemble.Sur la landing, le schéma vit dans env.schema.ts et se valide au build (vite.config.ts) : une clé absente fait échouer le build plutôt que le rendu, et zod ne part pas dans le navigateur.

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.
Ne jamais quoter les valeurs dans l’env passé à Docker/Dokploy.

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

Un transport pino tourne dans un worker thread qui charge son module depuis node_modules — absent des images standalone qui ne copient que dist/. Résultat : ModuleNotFound, crash. Seul pino-pretty (dev) passe par un transport ; l’export vers PostHog Logs (POSTHOG_LOGS_ENABLED=true) tourne dans le process et se bundle. Voir Logging.

Collision de casse (macOS ↔ Linux)

macOS est insensible à la casse, Linux (donc Docker/Dokploy) sensible. Un import ./WrappyView qui résout un fichier wrappy.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=true pour envoyer les logs à PostHog, 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. Pour les logs du serveur SSR, en Environment (runtime) : POSTHOG_PROJECT_TOKEN, POSTHOG_HOST, POSTHOG_LOGS_ENABLED=true, LOG_LEVEL. Sans eux, les logs restent sur stdout.
3

Reproduire en local