· Webentwicklung · 5 minuten Lesezeit
Vite Build-Time-Umgebungsvariablen in Docker: warum kein ARG nötig ist
import.meta.env-Variablen werden von Vite beim Build eingebettet, nicht zur Laufzeit gelesen. In Docker funktioniert das über den Build Context, nicht über ARG/ENV. Ein häufiger Fehler und die überraschend einfache Lösung.

Inhalt
- Das Missverständnis mit Vite-Umgebungsvariablen
- Das Problem mit Docker ARG
- Docker Compose env_file unter build ist nicht erlaubt
- Die korrekte Lösung mit Build Context
- Der einzige Fallstrick mit
no-cachenach Änderungen - Verifizierung
- Konsequenz .env gehört nicht in .gitignore für Docker-Projekte?
- Warum kein Root-.env
- Alle Artikel der Serie
Das Missverständnis mit Vite-Umgebungsvariablen
Wenn du in einem React + Vite Projekt import.meta.env.VITE_SOMETHING verwendest, hat das eine Eigenschaft, die sich von klassischen Node.js-Umgebungsvariablen fundamental unterscheidet:
Der Wert wird zur Build-Zeit eingebettet, nicht zur Laufzeit gelesen.
Vite ersetzt alle import.meta.env.VITE_*-Referenzen während npm run build durch deren Werte. Das erzeugte JavaScript-Bundle enthält dann die Werte direkt als Strings, ohne jede Referenz auf Umgebungsvariablen. Warum Vite als Bundler und nicht tsc für das Bauen zuständig ist, erkläre ich im Artikel über moduleResolution: bundler und noEmit.
// Source code:
const apiUrl = import.meta.env.VITE_API_URL;
// After the build (in dist/):
const apiUrl = 'http://localhost:3000';Das ist ein Feature, kein Bug. Es ermöglicht Tree-Shaking: Code-Pfade, die nur bei VITE_AUTH_ENABLED=false aktiv sind, landen nicht im Production-Bundle.
Das Problem mit Docker ARG
Wer das Vite-Verhalten kennt, kommt intuitiv auf Docker ARG/ENV:
# Attempt 1: ARG approach
FROM node:20-alpine AS builder
ARG VITE_AUTH_ENABLED=false
ENV VITE_AUTH_ENABLED=$VITE_AUTH_ENABLED
COPY . .
RUN npm run build# docker-compose.yml
services:
frontend:
build:
context: .
args:
VITE_AUTH_ENABLED: ${VITE_AUTH_ENABLED:-false}Das sieht richtig aus. Ist es aber nicht, wenn die Variablen aus einer per-Projekt .env-Datei kommen sollen.
Das Problem: ${VITE_AUTH_ENABLED:-false} in Docker Compose wird von der Shell aufgelöst, bevor Docker Compose die Datei verarbeitet. Wenn keine Umgebungsvariable VITE_AUTH_ENABLED in der Shell gesetzt ist, greift der Default-Wert false. Docker Compose liest an dieser Stelle kein per-Projekt .env — es liest nur Shell-Umgebungsvariablen und eine optionale Root-.env-Datei.
Das Ergebnis: VITE_AUTH_ENABLED ist im Docker-Build immer false, egal was in frontend/.env steht.
Docker Compose env_file unter build ist nicht erlaubt
Ein naheliegender nächster Versuch:
# Attempt 2: env_file under build (not supported)
services:
frontend:
build:
context: ./frontend
env_file: ./frontend/.env # ← FehlerDocker Compose v2.38.2 (und alle aktuellen Versionen) unterstützen env_file nicht unter dem build-Block:
additional properties 'env_file' not allowedenv_file funktioniert unter dem Service-Level (für Laufzeit-Variablen), nicht unter build (für Build-Zeit-Variablen). Das ist eine bekannte Einschränkung.
Die korrekte Lösung mit Build Context
Die Lösung ist überraschend einfach und braucht keinerlei Änderungen an Docker Compose oder dem Dockerfile.
Schritt 1: Prüfen, was im Build Context landet.
frontend/
├── .dockerignore
├── Dockerfile
├── package.json
├── src/
└── .env ← Wird von COPY . . einbezogen?# frontend/.dockerignore
node_modules/
dist/node_modules/ und dist/ sind ausgeschlossen. .env steht nicht in .dockerignore. Also wird .env von COPY . . in den Container kopiert.
Schritt 2: Vite liest .env automatisch.
FROM node:20-alpine AS builder
WORKDIR /app
COPY . . # ← .env ist jetzt im Container
RUN npm ci
RUN npm run build # ← Vite liest .env und bettet Werte einWenn Vite npm run build ausführt, sucht es automatisch nach .env, .env.local, .env.production usw. im Working Directory. Es findet frontend/.env (weil der Container-WORKDIR /app ist und COPY . . alles hineinkopiert hat) und liest die Werte.
Kein ARG, kein ENV, keine Compose-Konfiguration notwendig.
Der einzige Fallstrick mit no-cache nach Änderungen
Docker cached Build-Layer. Wenn frontend/.env sich ändert, aber keine anderen Dateien, kann Docker den gecachten Layer wiederverwenden und die neue .env nicht einlesen.
Nach Änderungen an frontend/.env immer mit --no-cache bauen:
docker compose build --no-cache frontend && docker compose up -d frontendOder den entsprechenden Layer invalidieren, indem eine andere Datei (z.B. package.json) geändert wird. Das ist weniger praktisch.
Verifizierung
Um sicherzustellen, dass die richtigen Werte im Bundle gelandet sind:
# Check whether AUTH_ENABLED is set correctly
grep -r "auth-disabled" dist/assets/
# Or positively: check whether the real client ID is in the bundle
grep -r "VITE_ZITADEL_CLIENT_ID" dist/assets/ # Should return no matches
grep -r "dein-client-id-wert" dist/assets/ # Should return matchesWenn "auth-disabled" im Bundle gefunden wird, obwohl VITE_AUTH_ENABLED=true in .env steht: Docker hat den gecachten Layer verwendet. --no-cache löst das.
Konsequenz .env gehört nicht in .gitignore für Docker-Projekte?
Nein. Die .env-Datei enthält Geheimnisse (Client-IDs, API-Keys) und gehört nicht ins Git-Repository.
Der Ansatz ist: .env.example committen, .env in .gitignore, CI/CD-Pipeline injiziert Werte über echte Secrets (GitHub Actions Secrets → frontend/.env vor dem Build schreiben).
# GitHub Actions example
- name: Create frontend .env
run: |
echo "VITE_AUTH_ENABLED=true" > frontend/.env
echo "VITE_ZITADEL_CLIENT_ID=${{ secrets.ZITADEL_CLIENT_ID }}" >> frontend/.env
echo "VITE_ZITADEL_DOMAIN=${{ secrets.ZITADEL_DOMAIN }}" >> frontend/.envDer Docker-Build läuft dann genauso wie lokal, nur dass .env von der CI-Pipeline erzeugt wird, nicht manuell.
Warum kein Root-.env
In vielen Projekten gibt es eine Root-.env, die alle Services konfiguriert. Das ist praktisch für den Start. In diesem Projekt gibt es bewusst per-Projekt-.env-Dateien:
frontend/.env— Vite-Variablen für das Frontendextension/.env— Vite-Variablen für die Chrome Extensionbackend/.env— Node.js-Variablen für das Backend
Jeder Service hat seinen eigenen Scope. Änderungen an Frontend-Variablen betreffen nicht das Backend-Dockerfile. docker compose build frontend reicht, kein Rebuild aller Services nötig.
Abbildung: Der Ablauf: docker compose build kopiert den Build Context (inkl. .env) in den Container. Dort fuehrt npm run build Vite aus. Vite liest .env automatisch und bettet alle VITE-Variablen in das JS-Bundle ein. Das fertige Bundle enthaelt keine Umgebungsvariablen-Referenzen mehr._
Alle Artikel der Serie
- Vision und Systemübersicht: Chrome Extension, RAG-Architektur, Projekthintergrund: Artikel lesen
- RAG-System Aufbau: Qdrant, Embeddings, Cosine-Ähnlichkeit in TypeScript: Artikel lesen
- AI Provider Abstraktion: Ollama vs. OpenAI, Interface-Design, kein Vendor-Lock-in: Artikel lesen
- Chrome Extension MV3: Drei isolierte Laufzeitkontexte, Message Passing, Strategy Pattern: Artikel lesen
- Docker Compose Strategie: Override-Pattern, von lokal zu Azure: Artikel lesen
- Ollama lokal vs. Docker: Die Entscheidung und ihre Konsequenzen: Artikel lesen
- Ollama Auto-Pull Entrypoint: Automatisiertes Modell-Setup beim Container-Start: Artikel lesen
- tsconfig und Vite:
Node16vs.bundler, warum Vite eigene Regeln hat: Artikel lesen - Instagram Caption mit MutationObserver vollständig laden: Artikel lesen
- Chrome Extension Foundation mit Health-Dot und Retry-Queue: Artikel lesen
- Phase 2 Features: Shadow DOM Overlay, Tailwind v4, Duplicate Detection: Artikel lesen
- Race Condition bei der Plattformerkennung: Wie ein UI-Event die Instagram-Erkennung bricht: Artikel lesen
- PostId-Extraktion in zwei Instagram-Layouts: querySelector vs. Ancestor-Traversal: Artikel lesen
- Instagram Karussell vollständig erfassen mit MutationObserver: Artikel lesen
- Notiz und Tags beim Screenshot-Speichern: Artikel lesen
- Instagram Tastatur-Shortcuts blockieren Chrome Extension Eingaben: Artikel lesen
- Lowercase-Normalisierung und Duplikat-Erkennung im Tag-Input: Artikel lesen
- Zitadel Login V2 in Docker Compose: drei versteckte Fehler: Artikel lesen
- PKCE OAuth in einer Chrome MV3 Extension: Artikel lesen
- React Frontend mit react-oidc-context und Zitadel: Artikel lesen
- Vite Build-Time-Umgebungsvariablen in Docker (dieser Artikel)
Du baust ein Vite-Frontend in Docker und kämpfst mit Umgebungsvariablen? Lass uns das gemeinsam einschätzen.



