Ευρετήριο ημερολογίουDockup / σημείωση πεδίου
Note / env-vars-not-reaching-container

Οι μεταβλητές περιβάλλοντος δεν φτάνουν στο container

Όταν οι μεταβλητές περιβάλλοντος δεν λειτουργούν σε ένα container, συνήθως φταίει ένας από πέντε λόγους: χρόνος build έναντι runtime, bundling frontend, εισαγωγικά, χρόνος επανεκκίνησης ή λάθος scope. Ελέγξτε τα με αυτή τη σειρά.

Ορίζετε τη μεταβλητή. Το dashboard την εμφανίζει. Η εφαρμογή λέει ότι είναι undefined. Το πρόβλημα των μεταβλητών περιβάλλοντος που δεν λειτουργούν σε ένα container είναι μία από τις συνηθέστερες αστοχίες configuration στο application hosting και σχεδόν πάντα οφείλεται σε ένα από πέντε συγκεκριμένα πράγματα.

Παρατίθενται εδώ με τη σειρά που επιτρέπει να εντοπίσετε το πρόβλημα πιο γρήγορα.

1. Ο χρόνος build και το runtime είναι δύο διαφορετικοί κόσμοι

Αυτό προκαλεί περισσότερα τέτοια προβλήματα από τα άλλα τέσσερα μαζί και είναι εκείνο που οι περισσότεροι βρίσκουν λιγότερο διαισθητικό.

Οι μεταβλητές που ορίζετε στην υπηρεσία σας υπάρχουν όταν το container εκτελείται. Ό,τι κάνει το Dockerfile συμβαίνει νωρίτερα, σε ξεχωριστό περιβάλλον. Ένα βήμα RUN δεν μπορεί να δει μια μεταβλητή runtime, επειδή εκείνη τη στιγμή δεν υπάρχει runtime.

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

Αν χρειάζεστε πραγματικά μια τιμή κατά το build, πρέπει να την περάσετε ως build argument — πρόκειται για διαφορετικό μηχανισμό, με διαφορετικές ιδιότητες ασφάλειας:

ARG BUILD_VERSION
RUN echo "Building $BUILD_VERSION"

Μην περνάτε ποτέ secret με αυτόν τον τρόπο. Τα build arguments καταγράφονται στο layer history του image. Όποιος μπορεί να κάνει pull το image μπορεί να τα διαβάσει.

2. Οι μεταβλητές του frontend ενσωματώνονται στο build, δεν διαβάζονται

Αν το frontend σας εμφανίζει undefined σε production, αυτός είναι σχεδόν σίγουρα ο λόγος.

Ο browser δεν έχει environment. Όταν γράφετε import.meta.env.VITE_API_URL ή process.env.NEXT_PUBLIC_API_URL, ο bundler αντικαθιστά το literal string κατά το build. Δεν γίνεται lookup στον browser — η τιμή έχει γίνει compile μέσα στον κώδικα.

Υπάρχουν τρεις συνέπειες που συχνά αιφνιδιάζουν:

  • Η αλλαγή της μεταβλητής δεν έχει κανένα αποτέλεσμα μέχρι να κάνετε rebuild. Η παλιά τιμή βρίσκεται μέσα στο JavaScript αρχείο.
  • Το prefix είναι υποχρεωτικό. Το Vite εκθέτει μόνο μεταβλητές με VITE_, ενώ το Next.js μόνο μεταβλητές με NEXT_PUBLIC_. Μια μεταβλητή χωρίς το αντίστοιχο prefix αποκρύπτεται σκόπιμα.
  • Οτιδήποτε εκτίθεται με αυτόν τον τρόπο είναι public. Βρίσκεται σε αρχείο που σερβίρετε σε οποιονδήποτε. Μην τοποθετείτε ποτέ secret πίσω από NEXT_PUBLIC_, ό,τι κι αν υποδηλώνει η ονομασία.

Αυτός είναι και ο λόγος που ένα prebuilt image δεν μπορεί να ρυθμιστεί με αυτόν τον τρόπο εκ των υστέρων. Αν το image δημιουργήθηκε αλλού και οι τιμές ενσωματώθηκαν κατά το build, ο ορισμός μεταβλητών στην υπηρεσία δεν αλλάζει τίποτα — τα strings βρίσκονται ήδη στο bundle.

3. Εισαγωγικά

Οι τιμές με ειδικούς χαρακτήρες παραμορφώνονται με τρόπους που προκαλούν μπερδεμένα errors αντί για προφανή σφάλματα.

# 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

Χαρακτήρες που προκαλούν αυτό το πρόβλημα είναι οι εξής: # (comment), $ (expansion), τα κενά (argument splitting), ! (history expansion σε interactive bash) και οι αλλαγές γραμμής — οι οποίες εμφανίζονται σε μία πολύ συνηθισμένη περίπτωση: τα private keys.

Οι multi-line τιμές προκαλούν τα περισσότερα προβλήματα. Ένα PEM key που επικολλάται σε πεδίο μίας γραμμής φτάνει χωρίς τις αλλαγές γραμμής του και προκαλεί parse error που δεν αναφέρει τίποτα για newlines. Κάντε Base64-encode την τιμή και κάντε decode μέσα στην εφαρμογή:

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

4. Δεν κάνατε restart

Οι μεταβλητές περιβάλλοντος διαβάζονται από μια διεργασία όταν ξεκινά. Η αλλαγή τους επηρεάζει την επόμενη διεργασία, όχι αυτή που εκτελείται ήδη.

Οι περισσότερες πλατφόρμες το αντιμετωπίζουν κάνοντας αυτόματα redeploy όταν αλλάζει το configuration, αλλά δεν το κάνουν όλες. Επιπλέον, μια μερική αλλαγή — ορίζετε τρεις μεταβλητές, κάνετε redeploy και ορίζετε μια τέταρτη — αφήνει μία μεταβλητή πίσω.

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

Ο έλεγχος που ξεκαθαρίζει την κατάσταση είναι ο εξής: διαβάστε τη μεταβλητή μέσα από το container που εκτελείται, όχι από το dashboard.

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

Αν εμφανίζεται σε αυτή την έξοδο και η εφαρμογή σας εξακολουθεί να λέει undefined, το πρόβλημα βρίσκεται στον κώδικά σας. Αν δεν εμφανίζεται στην έξοδο, το πρόβλημα βρίσκεται στο configuration. Αυτή η μία εντολή χωρίζει τον χώρο αναζήτησης στη μέση.

5. Λάθος scope

Οι μεταβλητές έχουν συνήθως scope — σε μια υπηρεσία, ένα environment ή ένα project. Μια μεταβλητή που έχει οριστεί στο production δεν είναι ορατή σε ένα preview environment. Ούτε μια μεταβλητή που έχει οριστεί σε διαφορετική υπηρεσία του ίδιου project είναι ορατή εδώ.

Αυτή είναι η συνηθέστερη αιτία όταν κάτι λειτουργεί σε ένα σημείο αλλά όχι σε κάποιο άλλο, παρόλο που ο κώδικας είναι ίδιος.

Η σειρά των διαγνωστικών ελέγχων

# 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

Ξεκινάτε πάντα από το βήμα 1. Μετατρέπει ένα ασαφές πρόβλημα σε ένα από δύο σαφή προβλήματα.

Ειδικά για τα secrets

Ανεξάρτητα από την πλατφόρμα, αξίζει να υιοθετήσετε δύο συνήθειες.

Σημειώστε τα secrets ως secrets. Στο Dockup, μια μεταβλητή που έχει σημειωθεί ως secret εμφανίζεται masked στις λίστες και στα API responses — το dockup env list εμφανίζει ******** αντί για την τιμή. Αυτό είναι πιο σημαντικό απ’ όσο φαίνεται, επειδή ο συνηθέστερος τρόπος διαρροής ενός credential δεν είναι μια επίθεση· είναι ένα screenshot, ένα support ticket ή μια γραμμή σε log.

Κρατήστε τα εκτός build arguments και frontend bundles. Και τα δύο είναι αναγνώσιμα από οποιονδήποτε αποκτήσει το artefact. Ο πρακτικός κανόνας είναι ο εξής: αν καταλήγει σε αρχείο που διανέμετε, δεν είναι πλέον secret.

Συχνές ερωτήσεις

Γιατί η μεταβλητή περιβάλλοντός μου είναι undefined κατά το build; Επειδή το build και το runtime είναι ξεχωριστά περιβάλλοντα. Οι μεταβλητές runtime δεν υπάρχουν όσο δημιουργείται το image. Χρησιμοποιήστε build argument αν χρειάζεστε πραγματικά μια τιμή κατά το build — ποτέ όμως secret.

Γιατί το frontend μου δεν βλέπει τη μεταβλητή; Οι bundlers αντικαθιστούν την τιμή κατά το build και εκθέτουν μόνο ονόματα με prefix — VITE_, NEXT_PUBLIC_. Η αλλαγή της μεταβλητής απαιτεί rebuild, ενώ οτιδήποτε εκτίθεται με αυτόν τον τρόπο είναι δημόσια αναγνώσιμο.

Χρειάζεται να κάνω restart μετά την αλλαγή μιας μεταβλητής; Ναι. Μια διεργασία που εκτελείται έχει ήδη διαβάσει το environment της. Οι περισσότερες πλατφόρμες κάνουν αυτόματα redeploy όταν αλλάζει μια μεταβλητή· επιβεβαιώστε το με printenv μέσα στο container αντί να βασιστείτε στο dashboard.

Πώς περνάω μια multi-line τιμή, όπως ένα private key; Κάντε Base64-encode την τιμή, ορίστε το encoded string και κάντε decode μέσα στην εφαρμογή. Τα πεδία environment μίας γραμμής αφαιρούν τις αλλαγές γραμμής και προκαλούν parse errors που δεν αναφέρουν τα newlines.