> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wrappy.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Variables d'environnement

> Comment l'env est consommé selon l'app (runtime vs build) et comment le configurer correctement en local, Docker et Dokploy.

## 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 ».

| App                         | Runtime               | Quand l'env est lu                                                 | Où fournir les vars          |
| --------------------------- | --------------------- | ------------------------------------------------------------------ | ---------------------------- |
| `api`                       | Hono / Bun            | **Au runtime** (`runtimeEnv: Bun.env`, lu au démarrage du serveur) | **Env runtime** du conteneur |
| `open`, `landing`, `studio` | Vite / TanStack Start | **Au build** — Vite inline `import.meta.env.VITE_*` dans le bundle | **Build args** du conteneur  |

<Info>
  Les deux valident via `@t3-oss/env-core` : une var **required manquante ou malformée** fait crasher au démarrage avec `❌ Invalid environment variables`.
</Info>

## 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).

<Warning>
  **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).
</Warning>

```dockerfile theme={null}
# ✅ API : aucun ARG pour les secrets. Juste NODE_ENV pour le mode.
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production          # chaque FROM reset les ENV → à (re)poser dans le stage final
COPY --from=installer /app/apps/api/dist ./apps/api/dist
WORKDIR /app/apps/api
CMD ["bun", "dist/server.js"]
```

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

```dockerfile theme={null}
# ✅ App Vite : chaque VITE_* consommée doit avoir son ARG + ENV avant le build
FROM base AS installer
ARG VITE_API_URL
ARG VITE_OPEN_URL
ENV VITE_API_URL=$VITE_API_URL
ENV VITE_OPEN_URL=$VITE_OPEN_URL
# …
RUN bun turbo run build --filter=@wrappy/open
```

<Warning>
  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.
</Warning>

## 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`.

<Tip>Ne jamais quoter les valeurs dans l'env passé à Docker/Dokploy.</Tip>

### `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.

<Tip>En prod, garder `POSTHOG_LOGS_ENABLED=false` : pino écrit alors en JSON sur stdout, que Docker/Dokploy capturent déjà.</Tip>

## 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

<Steps>
  <Step title="API">
    Toutes les vars en **Environment (runtime)**. Aucune en build arg. `NODE_ENV=production`, `POSTHOG_LOGS_ENABLED=false`, valeurs **sans guillemets**.
  </Step>

  <Step title="Apps Vite">
    Les `VITE_*` en **Build Arguments** (pas runtime). Vérifier que chaque var a son `ARG` dans le Dockerfile de l'app.
  </Step>

  <Step title="Reproduire en local">
    ```bash theme={null}
    # API — sans env, t3-env liste les vars manquantes
    docker build -f apps/api/Dockerfile -t wrappy-api .
    docker run --rm wrappy-api
    ```
  </Step>
</Steps>
