# Vite Dev Server - Technical Notes ## Problems & Solutions ### 1. Port Conflict Avoidance **Problem:** Don't want Vite's 5173 exposed on host (conflicts, security) **Solution:** Route everything through Traefik on port 18273 - Vite container: `expose: ["5173"]` (internal only, no `ports:` mapping) - Traefik label: `traefik.http.services.vite-dev.loadbalancer.server.port=5173` - Client connects to: `http://localhost:18273/phoenix/vite/...` ### 2. Path Prefixing **Problem:** Vite expects to serve from `/`, but we need `/phoenix/vite/` **Solution:** - **Vite CLI:** `vite --base /phoenix/vite/` in Dockerfile CMD - **Phoenix template transformation:** `scripts/docker/devops/Dockerfile.vite:34` ```dockerfile RUN sed -i "s|http://localhost:5173|/phoenix/vite|g" \ /phoenix/env/phoenix/server/templates/index.html ``` Transforms: ```html ``` Into: ```html ``` ### 3. HMR WebSocket Routing **Problem:** HMR WebSocket connects to port 18273, but Vite listens on 5173 **Solution:** Configure client port via env var - **docker-compose.yml:** `VITE_HMR_CLIENT_PORT=18273` - **vite.config.mts:** ```ts server: { hmr: process.env.VITE_HMR_CLIENT_PORT ? { clientPort: parseInt(process.env.VITE_HMR_CLIENT_PORT) } : true // Default behavior for local dev } ``` - Browser connects: `ws://localhost:18273/phoenix/vite/@vite/hmr` - Traefik routes to: `vite-dev:5173` container ### 4. Vite Host Validation **Problem:** Vite blocks requests from Docker hostnames Error: `Blocked request. This host ("vite-dev") is not allowed.` **Solution:** Disable host checking in config - **vite.config.mts:** ```ts server: { allowedHosts: "all" } ``` - **Vite CLI:** `--host 0.0.0.0` (listen on all interfaces) ### 5. Traefik Routing **Problem:** Route Vite assets without breaking Phoenix API/GraphQL routes **Solution:** Namespace separation with priority routing - **Vite routes (priority 100):** ```yaml traefik.http.routers.vite-dev.rule: Host(`localhost`) && PathPrefix(`/phoenix/vite`) traefik.http.routers.vite-dev.priority: 100 ``` - **Phoenix routes (default priority):** ```yaml traefik.http.routers.phoenix.rule: Host(`localhost`) && PathPrefix(`/phoenix`) ``` - Result: - `/phoenix/vite/*` → Vite (matches first, higher priority) - `/phoenix/openapi.json` → Phoenix - `/phoenix/graphql` → Phoenix ### 6. Source Code Isolation **Problem:** Don't want Docker-specific config polluting version control **Solution:** Environment variable pattern for optional overrides **app/vite.config.mts** (clean, works locally AND in Docker): ```ts server: { allowedHosts: "all", hmr: process.env.VITE_HMR_CLIENT_PORT ? { clientPort: parseInt(process.env.VITE_HMR_CLIENT_PORT) } : true, open: "http://localhost:6006" } ``` **Behavior:** - **Local dev:** No env var set → `hmr: true` (default port 5173) - **Docker:** `VITE_HMR_CLIENT_PORT=18273` → `hmr: { clientPort: 18273 }` ### 7. Volume Mount Conflicts **Problem:** Host's `node_modules` conflicts with container's **Solution:** Anonymous volume overlay ```yaml volumes: - ../../../app:/app:cached # Mount source code - /app/node_modules # Prevent host from overwriting ``` Container's `node_modules` takes precedence over host's. ## Architecture ``` Browser (localhost:18273) ↓ Traefik (reverse proxy) ├─ /phoenix/vite/* [priority:100] → vite-dev:5173 │ ├─ Vite dev server │ ├─ HMR WebSocket │ └─ ESM transformation │ └─ /phoenix/* [default] → phoenix:6006 ├─ FastAPI backend ├─ GraphQL API └─ Serves index.html (--dev mode) ``` **Request flow example:** 1. `GET /phoenix/` → Phoenix → Returns HTML with `