Nuxt Runtime Config vs. Build-Time Environment-Variablen
AI generated
{ }
Nuxt 3 · Konfiguration · Security
Nuxt Runtime Config vs. Build-Time Environment-Variablen
Wann welche Konfigurationsart die richtige Wahl ist, und wo die Sicherheitsrisiken liegen

runtimeConfig eignet sich für Werte, die sich nach dem Build noch ändern können, etwa weil derselbe Docker-Container in mehreren Umgebungen mit unterschiedlichen API-Endpunkten läuft. Der Unterschied zwischen public- und privaten Werten entscheidet dabei direkt darüber, ob ein Wert versehentlich als Secret im Client landet.

15 Min. Lesezeit runtimeConfig · Nuxt 3 Docker & Secrets

1. Build-Time und Runtime: Zwei unterschiedliche Zeitpunkte

Bei einer klassischen Frontend-Anwendung werden Environment-Variablen üblicherweise während des Build-Prozesses in den JavaScript-Code eingebacken. Ein Wert wie process.env.API_URL wird dabei vom Build-Tool durch den tatsächlichen String ersetzt, und dieser String ist danach fest im ausgelieferten Bundle verankert, ganz unabhängig davon, in welcher Umgebung das Bundle später tatsächlich läuft.

Das funktioniert gut, solange für jede Umgebung, etwa Staging und Produktion, ein eigener Build erstellt wird. Sobald aber derselbe, bereits gebaute Docker-Container in mehreren Umgebungen mit unterschiedlichen Konfigurationswerten laufen soll, reicht dieser Ansatz nicht mehr aus, da der Wert bereits zur Build-Zeit unveränderlich in den Code eingebrannt wurde und sich zur Laufzeit nicht mehr ändern lässt.

2. Das Konzept von runtimeConfig

Nuxt löst dieses Problem mit runtimeConfig, einer Konfigurationsstruktur, deren Werte nicht zur Build-Zeit fest eingebrannt, sondern beim Start des Servers aus den tatsächlichen Environment-Variablen des laufenden Prozesses gelesen werden. Ein und derselbe gebaute Container kann dadurch in unterschiedlichen Umgebungen mit unterschiedlichen Werten gestartet werden, ohne dass ein erneuter Build nötig ist.

Definiert wird runtimeConfig zentral in der nuxt.config.ts, wobei die dort hinterlegten Werte gleichzeitig als Standardwerte und als Typdefinition dienen. Zur Laufzeit überschreibt Nuxt jeden Wert automatisch, sobald eine passend benannte Environment-Variable gesetzt ist, wodurch sich Konfiguration und Code sauber voneinander trennen lassen.

3. Public vs. private Runtime-Config-Werte

Innerhalb von runtimeConfig unterscheidet Nuxt strikt zwischen Werten auf der obersten Ebene, die ausschließlich auf dem Server verfügbar sind, und Werten innerhalb des verschachtelten public-Schlüssels, die sowohl auf dem Server als auch im Client zugänglich sind. Diese Trennung ist keine reine Konvention, sondern wird von Nuxt technisch durchgesetzt, indem nur der public-Teil tatsächlich in das an den Client ausgelieferte JavaScript-Bundle eingebettet wird.

Im folgenden Beispiel ist der Datenbank-Zugangsschlüssel ausschließlich serverseitig verfügbar, während die öffentliche API-Basis-URL bewusst über public auch dem Client zur Verfügung gestellt wird, etwa für clientseitige $fetch-Aufrufe direkt aus dem Browser heraus.


// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // nur serverseitig verfügbar
    databaseApiKey: '',

    public: {
      // sowohl server- als auch clientseitig verfügbar
      apiBase: 'https://api.example.com',
    },
  },
});

// server/api/orders.get.ts
export default defineEventHandler((event) => {
  const config = useRuntimeConfig(event);
  // databaseApiKey ist hier verfügbar, im Client jedoch NICHT
  return $fetch(`${config.public.apiBase}/orders`, {
    headers: { Authorization: `Bearer ${config.databaseApiKey}` },
  });
});

4. Praxisfall: Docker-Container mit unterschiedlichen Umgebungen

In einem typischen Docker-basierten Deployment wird ein Image genau einmal gebaut und danach unverändert in mehreren Umgebungen wie Staging, Vorproduktion und Produktion ausgerollt. Würden API-Endpunkte oder Feature-Flags als Build-Time-Variablen behandelt, müsste für jede Umgebung ein eigenes Image gebaut werden, was den Grundsatz eines einmal gebauten, überall identischen Artefakts durchbricht.

Mit runtimeConfig bleibt das Image über alle Umgebungen hinweg identisch, lediglich die Environment-Variablen, die beim Start des Containers gesetzt werden, unterscheiden sich zwischen den Umgebungen. Das entspricht dem etablierten Prinzip, Konfiguration strikt von Code zu trennen, und reduziert gleichzeitig das Risiko, dass zwischen den Umgebungen unterschiedlicher Code getestet und ausgeliefert wird.

5. Namenskonvention für Environment-Variablen

Nuxt leitet den Namen der zugehörigen Environment-Variable automatisch aus dem verschachtelten Schlüsselpfad in runtimeConfig ab, wobei Großbuchstaben und Unterstriche verwendet werden. Ein Wert unter runtimeConfig.public.apiBase lässt sich beispielsweise über die Variable NUXT_PUBLIC_API_BASE setzen, während ein privater Wert wie runtimeConfig.databaseApiKey der Variable NUXT_DATABASE_API_KEY entspricht.

Diese automatische Ableitung reduziert Boilerplate erheblich, da kein manuelles Mapping zwischen Environment-Variablen und Konfigurationsstruktur gepflegt werden muss. In komplexeren Projekten mit vielen verschachtelten Konfigurationswerten empfiehlt es sich dennoch, die vollständige Liste der resultierenden Variablennamen zu dokumentieren, um Tippfehler beim Setzen der Variablen in der Deployment-Pipeline zu vermeiden.

6. Wann Build-Time-Variablen weiterhin nötig sind

Nicht jede Konfiguration eignet sich für runtimeConfig. Werte, die das Build-Ergebnis selbst beeinflussen, etwa welche Module aktiviert werden, welche CSS-Datei geladen wird oder ob bestimmter Code komplett aus dem Bundle entfernt werden soll, müssen zwangsläufig zur Build-Zeit bekannt sein, da sie das Ergebnis des Build-Prozesses selbst verändern und nicht erst nach dem Start des Servers relevant werden.

Für solche Fälle bleiben klassische Build-Time Environment-Variablen, die direkt innerhalb der nuxt.config.ts mit process.env ausgelesen werden, weiterhin der richtige Ansatz. Der entscheidende Unterschied liegt darin, ob ein Wert das Verhalten der bereits fertig gebauten Anwendung zur Laufzeit steuert, dann runtimeConfig, oder ob er den Build-Prozess selbst beeinflusst, dann eine klassische Build-Time-Variable.

7. Sicherheitsimplikationen bei exponierten Secrets

Der häufigste und gefährlichste Fehler im Umgang mit runtimeConfig besteht darin, einen sensiblen Wert wie einen API-Schlüssel, ein Datenbankpasswort oder ein privates Zertifikat versehentlich innerhalb des public-Schlüssels zu platzieren. Da alles innerhalb von public unverschlüsselt im an den Browser ausgelieferten JavaScript-Bundle landet, kann jeder Nutzer der Website diesen Wert einfach über die Browser-Entwicklertools einsehen.

Besonders tückisch ist, dass ein solcher Fehler während der lokalen Entwicklung meist unbemerkt bleibt, da alles scheinbar wie erwartet funktioniert, das Secret aber im Hintergrund bereits für jeden Website-Besucher sichtbar ist. Ein regelmäßiger Blick in das tatsächlich ausgelieferte Bundle, etwa über die Netzwerk-Ansicht der Browser-Entwicklertools, sowie eine bewusste Code-Review-Regel für alle Änderungen an runtimeConfig helfen, dieses Risiko zu minimieren.

8. Best Practices im Umgang mit runtimeConfig

Als Grundregel gilt: Jeder Wert startet als privater, serverseitiger Wert auf der obersten Ebene von runtimeConfig, und nur Werte, die nachweislich auch im Client benötigt werden, wandern bewusst und explizit in den public-Schlüssel. Diese defensive Grundhaltung verhindert, dass ein Wert aus Bequemlichkeit als public markiert wird, obwohl er es eigentlich gar nicht sein müsste.

Zusätzlich empfiehlt es sich, in der Deployment-Pipeline automatisiert zu prüfen, ob alle erwarteten Environment-Variablen beim Start des Containers tatsächlich gesetzt sind, um zu verhindern, dass die Anwendung mit leeren oder fehlenden Werten aus runtimeConfig in Produktion läuft. Ein fehlender, aber erwarteter Wert sollte den Deployment-Prozess idealerweise frühzeitig und klar sichtbar abbrechen lassen.

9. Fazit: Konfiguration bewusst nach Zeitpunkt trennen

Die Entscheidung zwischen runtimeConfig und klassischen Build-Time Environment-Variablen sollte sich stets danach richten, ob ein Wert das Verhalten des fertigen Builds zur Laufzeit steuert oder den Build-Prozess selbst beeinflusst. Für Docker-basierte Deployments mit einem einmal gebauten, in mehreren Umgebungen ausgerollten Image ist runtimeConfig in aller Regel die passendere und flexiblere Wahl.

Gleichzeitig verlangt runtimeConfig Disziplin bei der Trennung zwischen public- und privaten Werten, da ein einziger falsch platzierter Wert ausreicht, um ein Secret dauerhaft und für jeden Website-Besucher sichtbar zu exponieren. Wer diese Trennung konsequent einhält, gewinnt die Flexibilität von Runtime-Konfiguration, ohne die damit verbundenen Sicherheitsrisiken einzugehen.

Aspekt Build-Time Env-Variable runtimeConfig
Zeitpunkt der Auflösung Während des Build-Prozesses Beim Start des Servers zur Laufzeit
Änderung ohne Rebuild Nicht möglich Möglich, über neu gesetzte Env-Variablen
Typischer Einsatz Aktivierte Module, CSS-Auswahl API-Endpunkte, Feature-Flags, Secrets
Docker-Eignung Ein Image pro Umgebung nötig Ein Image für alle Umgebungen
Sicherheitsrisiko Wert steht im Quellcode/Build Nur bei falscher public-Platzierung riskant

Mironsoft

Vue-Architektur, Composition API und Nuxt-Performance

Vue-Anwendungen, die mit jedem Feature nicht komplizierter werden?

Wir prüfen bestehende Vue- und Nuxt-Projekte auf unstrukturierte Composables, ungenutzte Reaktivität und aufgeblähte Bundles und bauen daraus eine Architektur, die neue Features aufnimmt, ohne die Codebasis unübersichtlicher zu machen.

Architektur-Review

Composables, State-Management und Komponentenstruktur auf Wartbarkeit prüfen.

Performance-Audit

Reaktivitäts-Overhead, Bundle-Größe und Nuxt-Rendering-Strategie systematisch optimieren.

Nuxt-Integration

SSR/SSG-Setup und API-Anbindung robust und typsicher aufbauen.

10. Zusammenfassung

Runtime Config vs. Build-Time Env: Das Wichtigste auf einen Blick

runtimeConfig

Werte, die sich nach dem Build noch ändern können, aus Env gelesen

public-Schlüssel

Einzige Werte, die tatsächlich in den Client-Bundle gelangen

Docker-Vorteil

Ein Image für alle Umgebungen, Konfiguration über Env-Variablen

Größtes Risiko

Secret versehentlich im public-Schlüssel statt privat platziert

11. FAQ: Runtime Config vs. Build-Time Env: Das Wichtigste auf einen Blick

1Was ist der Kernunterschied zwischen runtimeConfig und process.env in nuxt.config.ts?
process.env innerhalb der nuxt.config.ts wird zur Build-Zeit ausgewertet und beeinflusst das Build-Ergebnis selbst. runtimeConfig-Werte werden dagegen beim Start des Servers aus den dann aktuellen Environment-Variablen gelesen und können sich ohne neuen Build ändern.
2Warum landet ein Wert aus dem public-Schlüssel im Client-Bundle?
Nuxt bettet den gesamten Inhalt von runtimeConfig.public bewusst in das an den Browser ausgelieferte JavaScript ein, damit clientseitiger Code, etwa für $fetch-Aufrufe, auf diese Werte zugreifen kann. Alles außerhalb von public bleibt ausschließlich serverseitig.
3Wie greife ich in einer Komponente auf runtimeConfig zu?
Über das Composable useRuntimeConfig(). Innerhalb einer Vue-Komponente, die sowohl server- als auch clientseitig ausgeführt wird, sollte darauf geachtet werden, nur auf Werte innerhalb von public zuzugreifen, da private Werte im Client ohnehin undefined wären.
4Kann ich runtimeConfig-Werte per Docker Compose setzen?
Ja, dafür reicht es, die entsprechend benannten Environment-Variablen, etwa NUXT_PUBLIC_API_BASE, im environment-Block von Docker Compose oder in einer .env-Datei zu setzen, die beim Start des Containers geladen wird.
5Was passiert, wenn eine erwartete Environment-Variable fehlt?
Nuxt fällt in diesem Fall auf den in der nuxt.config.ts definierten Standardwert zurück. Fehlt sowohl die Environment-Variable als auch ein sinnvoller Standardwert, sollte die Anwendung idealerweise durch eine eigene Prüfung beim Start klar auf den fehlenden Wert hinweisen.
6Ist runtimeConfig auch für statisch generierte Seiten (SSG) sinnvoll?
Bei vollständig statisch generierten Seiten ohne laufenden Server verliert runtimeConfig einen Teil seines Nutzens, da es keinen Serverstart mehr gibt, der Environment-Variablen neu einliest. Für server-gerenderte oder hybride Deployments bleibt der Ansatz dagegen voll wirksam.
7Wie finde ich heraus, welche Environment-Variable zu welchem runtimeConfig-Wert gehört?
Nuxt leitet den Variablennamen automatisch aus dem verschachtelten Schlüsselpfad ab, in Großbuchstaben mit Unterstrichen und dem Präfix NUXT_. Bei Unklarheiten hilft ein Blick in die offizielle Nuxt-Dokumentation oder ein Debug-Log des aufgelösten Konfigurationsobjekts.
8Kann ein Wert sowohl privat als auch public gleichzeitig existieren?
Nicht direkt unter demselben Schlüssel, aber es ist möglich, zwei getrennte Werte mit unterschiedlichem Inhalt zu definieren, etwa einen vollen, privaten API-Schlüssel und eine öffentliche, eingeschränkte Variante desselben Dienstes.
9Wie erkenne ich im Nachhinein, ob ein Secret versehentlich exponiert wurde?
Ein direkter Blick in das ausgelieferte JavaScript-Bundle im Browser, etwa über die Entwicklertools oder durch Durchsuchen der Build-Ausgabe nach bekannten Secret-Mustern, zeigt zuverlässig, ob ein Wert tatsächlich im Client gelandet ist.
10Muss ich runtimeConfig für jeden noch so kleinen Konfigurationswert verwenden?
Nein, für Werte, die sich nie zwischen Umgebungen unterscheiden und das Verhalten der Anwendung nicht steuern, reicht eine feste Konstante im Code oft vollkommen aus. runtimeConfig lohnt sich vor allem für tatsächlich umgebungsabhängige Werte.