JournalindexDockup / fältanteckning
Note / env-vars-not-reaching-container

Miljövariabler når inte containern

Miljövariabler som inte fungerar i en container beror oftast på en av fem orsaker: build time kontra runtime, frontend-bundling, citationstecken, omstartstidpunkt eller fel scope. Kontrollera dem i den här ordningen.

Du har angett variabeln. Dashboarden visar den. Applikationen säger att den är undefined. Miljövariabler som inte fungerar i en container är ett av de vanligaste konfigurationsfelen vid hosting av applikationer, och det beror nästan alltid på en av fem specifika saker.

De listas här i den ordning som snabbast hittar problemet.

1. Build time och runtime är två olika världar

Det här orsakar fler av dessa problem än de fyra andra tillsammans, och är det som flest tycker är minst intuitivt.

Variabler som anges för din service finns när containern körs. Allt som ditt Dockerfile gör sker tidigare, i en separat miljö. Ett RUN-steg kan inte se en runtime-variabel, eftersom det inte finns någon runtime i det ögonblicket.

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

Om du verkligen behöver ett värde under build måste det skickas som ett build-argument — vilket är en annan mekanism med andra säkerhetsegenskaper:

ARG BUILD_VERSION
RUN echo "Building $BUILD_VERSION"

Skicka aldrig en hemlighet på det här sättet. Build-argument sparas i imagets lagerhistorik. Alla som kan hämta imagen kan läsa dem.

2. Frontend-variabler byggs in, de läses inte in

Om ditt frontend-projekt säger undefined i produktion är det nästan säkert därför.

En webbläsare har ingen environment. När du skriver import.meta.env.VITE_API_URL eller process.env.NEXT_PUBLIC_API_URL ersätter bundlern det med den bokstavliga strängen vid build time. Det sker ingen uppslagning i webbläsaren — värdet kompilerades in.

Tre konsekvenser som ofta överraskar:

  • Att ändra variabeln gör ingenting förrän du bygger om. Det gamla värdet finns inuti JavaScript-filen.
  • Prefixet är obligatoriskt. Vite exponerar endast VITE_, och Next.js exponerar endast NEXT_PUBLIC_. En variabel utan prefix hålls avsiktligt dold.
  • Allt som exponeras på det här sättet är publikt. Det finns i en fil som du levererar till vem som helst. Lägg aldrig en hemlighet bakom NEXT_PUBLIC_, oavsett vad namnet antyder.

Det är också därför en färdigbyggd image inte kan konfigureras på det här sättet i efterhand. Om imagen byggdes någon annanstans med värdena inbakade ändrar det ingenting att ange variabler på servicen — strängarna finns redan i bundlen.

3. Citationstecken

Värden med specialtecken förvanskas på sätt som ger förvirrande fel i stället för uppenbara fel.

# 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

Tecken som orsakar detta är: # (kommentar), $ (expansion), mellanslag (uppdelning av argument), ! (historikexpansion i interaktiv bash) och radbrytningar — som förekommer i exakt ett vanligt fall: privata nycklar.

Flerradiga värden är det värsta problemet. En PEM-nyckel som klistras in i ett enradsfält kommer fram utan sina radbrytningar och orsakar ett parserfel som inte säger något om radbrytningar. Base64-koda den och avkoda den i applikationen:

dockup env set "PRIVATE_KEY_B64=$(base64 -i key.pem)" my-project/my-api

4. Du startade inte om

Miljövariabler läses av en process när den startar. När du ändrar dem påverkas nästa process, inte den som körs just nu.

De flesta plattformar hanterar detta genom att automatiskt göra en ny deployment när konfigurationen ändras, men alla gör inte det, och en deländring — ange tre variabler, gör en deployment, ange en fjärde — lämnar en variabel kvar.

dockup env list my-project/my-api --json   # what is configured
dockup restart my-project/my-api            # make the process re-read it

Kontrollen som avgör saken: läs variabeln inifrån den körande containern, inte från dashboarden.

dockup exec "printenv | sort" my-project/my-api

Om den finns i resultatet men appen fortfarande säger undefined ligger problemet i din kod. Om den inte finns i resultatet ligger problemet i konfigurationen. Det enda kommandot delar sökrymden på mitten.

5. Fel scope

Variabler är vanligtvis begränsade till en service, en miljö eller ett projekt. En variabel som angetts i produktion är inte synlig i en preview-miljö. En variabel som angetts för en annan service i samma projekt är inte heller synlig.

Det här är den vanligaste orsaken när något fungerar på en plats men inte på en annan, trots identisk kod.

Diagnostisk ordning

# 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

Börja alltid med steg 1. Det omvandlar ett tvetydigt problem till ett av två entydiga problem.

Särskilt om hemligheter

Två vanor är värda att införa oavsett plattform.

Markera hemligheter som hemligheter. I Dockup maskeras en variabel som markerats som hemlig i listningar och API-svar — dockup env list visar ******** i stället för värdet. Det är viktigare än det kanske låter, eftersom det vanligaste sättet som en credential läcker på inte är en attack; det är en skärmdump, ett supportärende eller en loggrad.

Håll dem borta från build-argument och frontend-bundlar. Båda kan läsas av alla som får tag på artefakten. Tumregeln är: om det hamnar i en fil som du distribuerar är det inte längre en hemlighet.

Vanliga frågor

Varför är min miljövariabel undefined vid build time? Eftersom build och runtime är separata miljöer. Runtime-variabler finns inte medan imagen byggs. Använd ett build-argument om du verkligen behöver ett värde under build — och aldrig en hemlighet.

Varför ser mitt frontend-projekt inte variabeln? Bundlers ersätter värdet vid build time och exponerar endast namn med prefix — VITE_, NEXT_PUBLIC_. Om du ändrar variabeln måste du bygga om, och allt som exponeras på det här sättet kan läsas publikt.

Måste jag starta om efter att ha ändrat en variabel? Ja. En process som redan körs har redan läst sin environment. De flesta plattformar gör automatiskt en ny deployment vid ändringar; verifiera med printenv inuti containern i stället för att lita på dashboarden.

Hur skickar jag ett flerradigt värde, till exempel en privat nyckel? Base64-koda det, ange den kodade strängen och avkoda den i applikationen. Enradsfält för miljövariabler tar bort radbrytningar och orsakar parserfel som inte nämner radbrytningar.