JournalindeksDockup / feltnotat
Note / env-vars-not-reaching-container

Miljøvariabler når ikke frem til containeren

Miljøvariabler som ikke fungerer i en container, skyldes vanligvis én av fem ting: build-tid kontra kjøretid, frontend-bundling, sitering, omstartstidspunkt eller feil omfang. Sjekk dem i denne rekkefølgen.

Du angir variabelen. Dashboardet viser den. Applikasjonen sier at den er undefined. Miljøvariabler som ikke fungerer i en container er en av de vanligste konfigurasjonsfeilene ved hosting av applikasjoner, og skyldes nesten alltid én av fem konkrete ting.

De er listet opp i den rekkefølgen som vanligvis finner problemet raskest.

1. Build-tid og kjøretid er to forskjellige verdener

Dette forårsaker flere slike problemer enn de fire andre til sammen, og er samtidig det folk synes er minst intuitivt.

Variabler du angir på tjenesten, finnes når containeren kjører. Alt Dockerfile-filen gjør, skjer tidligere og i et separat miljø. Et RUN-steg kan ikke se en runtime-variabel, fordi det ikke finnes noen runtime på det tidspunktet.

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

Hvis du faktisk trenger en verdi under build, må den sendes inn som et build-argument — som er en annen mekanisme med andre sikkerhetsegenskaper:

ARG BUILD_VERSION
RUN echo "Building $BUILD_VERSION"

Send aldri en hemmelighet på denne måten. Build-argumenter lagres i laghistorikken til imaget. Alle som kan hente imaget, kan lese dem.

2. Frontend-variabler bygges inn, de leses ikke

Hvis frontend-applikasjonen din sier undefined i produksjon, er dette nesten helt sikkert årsaken.

En nettleser har ikke noe miljø. Når du skriver import.meta.env.VITE_API_URL eller process.env.NEXT_PUBLIC_API_URL, erstatter bundleren uttrykket med den bokstavelige strengen under build. Det skjer ikke noe oppslag i nettleseren — verdien ble kompilert inn.

Dette har tre konsekvenser som ofte overrasker folk:

  • Det hjelper ikke å endre variabelen før du bygger på nytt. Den gamle verdien ligger allerede i JavaScript-filen.
  • Prefikset er obligatorisk. Vite eksponerer bare VITE_, mens Next.js bare eksponerer NEXT_PUBLIC_. En variabel uten prefiks holdes bevisst skjult.
  • Alt som eksponeres på denne måten, er offentlig. Verdien ligger i en fil du serverer til hvem som helst. Legg aldri en hemmelighet bak NEXT_PUBLIC_, uansett hva navnet antyder.

Dette er også grunnen til at et ferdigbygd image ikke kan konfigureres på denne måten i etterkant. Hvis imaget ble bygget et annet sted med verdiene kompilert inn, endrer det ingenting å angi variabler på tjenesten — strengene ligger allerede i bundlen.

3. Sitering

Verdier med spesialtegn kan bli forvansket på måter som fører til forvirrende feil i stedet for åpenbare feil.

# 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

Tegn som kan forårsake dette, er # (kommentar), $ (ekspandering), mellomrom (oppdeling av argumenter), ! (historikk-ekspandering i interaktiv bash) og linjeskift — som forekommer i akkurat ett vanlig tilfelle: private nøkler.

Flere linjer i én verdi er det verste tilfellet. En PEM-nøkkel som limes inn i et felt på én linje, kommer frem uten linjeskiftene og fører til en parse-feil som ikke sier noe om linjeskift. Base64-kod den og dekod den i applikasjonen:

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

4. Du startet ikke på nytt

Miljøvariabler leses av en prosess når den starter. Når du endrer dem, påvirker det neste prosess, ikke prosessen som kjører nå.

De fleste plattformer håndterer dette ved å kjøre en ny deployment automatisk når konfigurasjonen endres, men ikke alle gjør det. En delvis endring — angi tre variabler, kjør ny deployment, angi en fjerde — kan også føre til at én blir hengende igjen.

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 avgjør saken: Les variabelen fra innsiden av den kjørende containeren, ikke fra dashboardet.

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

Hvis variabelen finnes i utskriften og appen fortsatt sier undefined, ligger problemet i koden. Hvis den ikke finnes i utskriften, ligger problemet i konfigurasjonen. Den ene kommandoen deler søkeområdet i to.

5. Feil omfang

Variabler har vanligvis et omfang — en tjeneste, et miljø eller et prosjekt. En variabel som er angitt i produksjon, er ikke synlig i et preview-miljø. En variabel som er angitt på en annen tjeneste i samme prosjekt, er heller ikke synlig.

Dette er den vanligste årsaken når noe fungerer ett sted, men ikke et annet, selv om koden er identisk.

Diagnostisk rekkefølge

# 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

Start med steg 1, hver gang. Det gjør et uklart problem om til ett av to entydige problemer.

Spesielt om hemmeligheter

Det er verdt å innarbeide to vaner, uavhengig av plattform.

Merk hemmeligheter som hemmeligheter. På Dockup blir en variabel som er merket som hemmelighet, maskert i lister og API-svar — dockup env list viser ******** i stedet for verdien. Det er viktigere enn det kanskje høres ut som, fordi den vanligste måten en påloggingsopplysning lekker på, ikke er et angrep; det er et skjermbilde, en supportsak eller en logglinje.

Hold dem borte fra build-argumenter og frontend-bundler. Begge deler kan leses av alle som får tilgang til artefakten. Tommelfingerregelen er: Hvis den ender opp i en fil du distribuerer, er den ikke lenger en hemmelighet.

Vanlige spørsmål

Hvorfor er miljøvariabelen min udefinert under build? Fordi build og kjøretid er separate miljøer. Runtime-variabler finnes ikke mens imaget bygges. Bruk et build-argument hvis du faktisk trenger en verdi under build — og aldri en hemmelighet.

Hvorfor finner ikke frontend-applikasjonen min variabelen? Bundlere erstatter verdien under build og eksponerer bare navn med prefiks — VITE_, NEXT_PUBLIC_. Når du endrer variabelen, må du bygge på nytt, og alt som eksponeres på denne måten, kan leses offentlig.

Må jeg starte på nytt etter at jeg har endret en variabel? Ja. En prosess som allerede kjører, har allerede lest miljøet sitt. De fleste plattformer kjører automatisk en ny deployment ved endringer; verifiser med printenv inne i containeren i stedet for å stole på dashboardet.

Hvordan sender jeg inn en verdi over flere linjer, for eksempel en privat nøkkel? Base64-kod den, angi den kodede strengen og dekod den i applikasjonen. Miljøfelt på én linje fjerner linjeskift og fører til parse-feil som ikke nevner linjeskift.