Configuration
Last updated
Configuration belongs to the deployment, not to the image. That separation is what lets you change a secret without rebuilding, and run the same image with different settings.
Two mechanisms
- Environment variables — the default for almost everything, including secrets.
- Config files — for tools that insist on reading a file from a path.
Both are applied when the container is created, which means changing either recreates the container. Batch your changes.
Configuration is not available during the build
This catches people repeatedly, so it's worth stating plainly: the image build receives no environment variables and no build arguments.
Consequences:
- An
ARGin your Dockerfile with no default resolves to empty. - Anything a framework inlines at build time —
NEXT_PUBLIC_*in Next.js,VITE_*in Vite, similar prefixes elsewhere — is baked in when the image is built, so setting it on the app has no effect on the browser bundle. There is no error; the value simply isn't there.
If you need build-time values, build the image in CI where you can set them, and push the image. Otherwise read configuration at runtime, which is what most frameworks recommend anyway.
Secrets
Set them as environment variables on the app. Never put them in:
- Your source code, which is likely to end up in a repository.
- The Dockerfile.
ENVvalues live in image layers and are readable by anyone who can pull the image, without running it. - A client-side prefix. Anything prefixed
NEXT_PUBLIC_orVITE_is compiled into JavaScript sent to the browser.
If a secret has already been committed or built into an image, treat it as public and issue a new one — removing it from a later commit does not un-publish it.
Reserved names and limits
- Names beginning with
DF_are reserved. The platform injects several, including the app's own public URLs, and will reject your own variables using that prefix. - Values may be large and may contain newlines, so multi-line values such as private keys work as a single variable.
- Config files are mounted read-only — your app can read them but not write to them — and cannot be mounted over system paths.
Fail loudly when configuration is missing
Read required values at startup and exit with a clear message if they're absent:
TOKEN = os.environ.get("BOT_TOKEN")
if not TOKEN:
sys.exit("BOT_TOKEN is not set - set it as an environment variable on the app")
That turns a confusing runtime error into an obvious one, and puts an exit code in the app's events where you can see it.