Umgebungsvariablen erreichen den Container nicht
Wenn Umgebungsvariablen in einem Container nicht funktionieren, liegt es meist an einem von fünf Gründen: Build-Zeit gegenüber Laufzeit, Frontend-Bundling, Anführungszeichen, fehlender Neustart oder dem falschen Geltungsbereich. Prüfen Sie diese Ursachen der Reihe nach.
Sie setzen die Variable. Im Dashboard wird sie angezeigt. Die Anwendung meldet, dass sie undefined ist. Nicht funktionierende Umgebungsvariablen in einem Container gehören zu den häufigsten Konfigurationsfehlern beim Hosting von Anwendungen und haben fast immer eine von fünf konkreten Ursachen.
Sie sind hier in der Reihenfolge aufgeführt, in der sich das Problem am schnellsten finden lässt.
1. Build-Zeit und Laufzeit sind zwei verschiedene Welten
Diese Ursache ist häufiger als die anderen vier zusammen und wird am wenigsten intuitiv verstanden.
Variablen, die für Ihren Service gesetzt sind, existieren, wenn der Container läuft. Alles, was Ihr Dockerfile ausführt, geschieht früher und in einer separaten Umgebung. Ein RUN-Schritt kann keine Laufzeitvariable sehen, weil es zu diesem Zeitpunkt noch keine Laufzeit gibt.
# This is empty during build. Always.
RUN echo $DATABASE_URL
# This is available at runtime, because it is the running process reading it
CMD ["node", "server.js"]
Wenn Sie einen Wert tatsächlich während des Builds benötigen, muss er als Build-Argument übergeben werden – dabei handelt es sich um einen anderen Mechanismus mit anderen Sicherheitseigenschaften:
ARG BUILD_VERSION
RUN echo "Building $BUILD_VERSION"
Übergeben Sie auf diese Weise niemals ein Secret. Build-Argumente werden in der Layer-Historie des Images aufgezeichnet. Jeder, der das Image herunterladen kann, kann sie auslesen.
2. Frontend-Variablen werden fest einkompiliert und nicht ausgelesen
Wenn Ihr Frontend in der Produktion undefined meldet, ist dies mit hoher Wahrscheinlichkeit der Grund.
Ein Browser hat keine Umgebung. Wenn Sie import.meta.env.VITE_API_URL oder process.env.NEXT_PUBLIC_API_URL schreiben, ersetzt der Bundler den Ausdruck zur Build-Zeit durch den konkreten String. Im Browser findet keine Suche statt – der Wert wurde einkompiliert.
Daraus ergeben sich drei Folgen, die häufig übersehen werden:
- Eine Änderung der Variable bewirkt nichts, bis Sie neu bauen. Der alte Wert befindet sich in der JavaScript-Datei.
- Das Präfix ist zwingend erforderlich. Vite stellt nur
VITE_-Variablen bereit, Next.js nurNEXT_PUBLIC_. Eine Variable ohne das Präfix wird absichtlich nicht veröffentlicht. - Alles, was auf diese Weise veröffentlicht wird, ist öffentlich. Der Wert befindet sich in einer Datei, die Sie jedem ausliefern. Legen Sie niemals ein Secret hinter
NEXT_PUBLIC_, ganz gleich, was der Name nahelegt.
Deshalb kann ein fertig gebautes Image nachträglich auch nicht auf diese Weise konfiguriert werden. Wenn das Image an anderer Stelle gebaut und die Werte dabei einkompiliert wurden, ändert das Setzen von Variablen am Service nichts – die Strings befinden sich bereits im Bundle.
3. Anführungszeichen
Werte mit Sonderzeichen werden auf eine Weise verändert, die eher verwirrende Fehler als offensichtliche Fehlermeldungen erzeugt.
# The shell eats everything after #
dockup env set DB_PASS=p@ss#word my-project/my-api
# Quote it
dockup env set 'DB_PASS=p@ss#word' my-project/my-api
Zu den problematischen Zeichen gehören: # (Kommentar), $ (Expansion), Leerzeichen (Aufteilung in Argumente), ! (History-Expansion in einer interaktiven Bash) und Zeilenumbrüche – die genau in einem häufigen Fall vorkommen: bei privaten Schlüsseln.
Mehrzeilige Werte verursachen die größten Probleme. Ein in ein einzeiliges Feld eingefügter PEM-Schlüssel kommt ohne seine Zeilenumbrüche an und erzeugt einen Parse-Fehler, der nichts über Zeilenumbrüche aussagt. Kodieren Sie den Wert mit Base64 und dekodieren Sie ihn in der Anwendung:
dockup env set "PRIVATE_KEY_B64=$(base64 -i key.pem)" my-project/my-api
4. Sie haben keinen Neustart durchgeführt
Umgebungsvariablen werden von einem Prozess beim Start eingelesen. Eine Änderung wirkt sich auf den nächsten Prozess aus, nicht auf den aktuell laufenden.
Die meisten Plattformen lösen bei Änderungen an der Konfiguration automatisch ein neues Deployment aus, aber nicht alle. Außerdem kann eine teilweise vorgenommene Änderung – drei Variablen setzen, neu deployen, eine vierte setzen – dazu führen, dass eine Variable fehlt.
dockup env list my-project/my-api --json # what is configured
dockup restart my-project/my-api # make the process re-read it
Die entscheidende Prüfung: Lesen Sie die Variable aus dem laufenden Container aus, nicht aus dem Dashboard.
dockup exec "printenv | sort" my-project/my-api
Wenn sie in dieser Ausgabe enthalten ist und Ihre Anwendung weiterhin undefined meldet, liegt das Problem in Ihrem Code. Wenn sie in der Ausgabe fehlt, liegt das Problem in der Konfiguration. Dieser eine Befehl halbiert den möglichen Suchbereich.
5. Falscher Geltungsbereich
Variablen gelten normalerweise für einen bestimmten Service, eine Umgebung oder ein Projekt. Eine in der Produktion gesetzte Variable ist in einer Preview-Umgebung nicht sichtbar. Eine Variable, die für einen anderen Service im selben Projekt gesetzt wurde, ist dort ebenfalls nicht sichtbar.
Das ist die häufigste Ursache, wenn etwas an einer Stelle funktioniert und an einer anderen mit identischem Code nicht.
Die diagnostische Reihenfolge
# 1. Is it actually in the container's environment?
dockup exec "printenv | sort" my-project/my-api
# 2. Is it configured on the service you think it is?
dockup env list my-project/my-api --json
# 3. Is the running process older than the change?
dockup status my-project/my-api --json
Beginnen Sie jedes Mal mit Schritt 1. So wird aus einem uneindeutigen Problem eines von zwei eindeutig bestimmbaren Problemen.
Speziell bei Secrets
Unabhängig von der Plattform lohnen sich zwei Gewohnheiten.
Markieren Sie Secrets als Secrets. Bei Dockup wird eine als Secret markierte Variable in Auflistungen und API-Antworten maskiert – dockup env list zeigt ******** statt des Werts. Das ist wichtiger, als es zunächst klingt, denn am häufigsten wird ein Zugangsschlüssel nicht durch einen Angriff offengelegt, sondern durch einen Screenshot, ein Support-Ticket oder eine Logzeile.
Halten Sie sie aus Build-Argumenten und Frontend-Bundles heraus. Beides kann von jedem ausgelesen werden, der das Artefakt erhält. Als Faustregel gilt: Wenn ein Wert in einer Datei landet, die Sie verteilen, ist er kein Secret mehr.
Häufig gestellte Fragen
Warum ist meine Umgebungsvariable zur Build-Zeit nicht definiert? Weil Build und Laufzeit getrennte Umgebungen sind. Laufzeitvariablen existieren während des Image-Builds nicht. Verwenden Sie ein Build-Argument, wenn Sie während des Builds tatsächlich einen Wert benötigen – aber niemals ein Secret.
Warum erkennt mein Frontend die Variable nicht?
Bundler ersetzen den Wert zur Build-Zeit und veröffentlichen nur Variablen mit bestimmten Präfixen – VITE_, NEXT_PUBLIC_. Eine Änderung der Variable erfordert einen neuen Build, und alles, was auf diese Weise veröffentlicht wird, ist öffentlich auslesbar.
Muss ich nach der Änderung einer Variable neu starten?
Ja. Ein laufender Prozess hat seine Umgebung bereits eingelesen. Die meisten Plattformen führen bei Änderungen automatisch ein neues Deployment durch. Prüfen Sie dies mit printenv im Container, statt dem Dashboard zu vertrauen.
Wie übergebe ich einen mehrzeiligen Wert wie einen privaten Schlüssel? Kodieren Sie ihn mit Base64, setzen Sie den kodierten String und dekodieren Sie ihn in der Anwendung. Einzeilige Umgebungsvariablenfelder entfernen Zeilenumbrüche und erzeugen Parse-Fehler, die Zeilenumbrüche nicht erwähnen.
