By default Unkey builds your application automatically; you don't need a Dockerfile. When your build needs more control, for example system packages, custom build steps, or a toolchain detection doesn't cover, add a Dockerfile and Unkey builds with it instead.
If you're not comfortable writing a Dockerfile from scratch, the AI Dockerfile prompt generates one tuned for Unkey's runtime.
Switching to Dockerfile builds#
Configure the Dockerfile path in your app settings. Unkey auto-detects Dockerfiles in your build context and offers them in the dropdown. Once a path is set, every deployment of that app and environment builds with the Dockerfile.
To switch back to automatic builds, set the Dockerfile field to Automatic (no Dockerfile). Changes apply on the next deployment.
Build configuration#
| Setting | Description | Default |
|---|---|---|
| Root directory | The build context directory, where COPY and ADD are relative to | . (repo root) |
| Dockerfile | Path to the Dockerfile within the root directory | Automatic |
See App settings for the full reference.
Build-time environment variables#
Some builds need access to environment variables during the build step, for example to install private packages or generate code. All variables configured for the environment are available as Docker build secrets.
Unkey mounts all your variables as a .env file at /run/secrets/.env inside the build container. To use them, add a --mount=type=secret flag to the RUN step that needs them. Unkey exposes a build argument called UNKEY_SECRETS_ID that you reference as the mount's id (see Variable changes and the build cache).
Here's a complete example for a Node.js app that needs environment variables during the build step:
Declare ARG UNKEY_SECRETS_ID inside every stage that mounts the secret. ARG values aren't inherited across stages, so each FROM ... AS <stage> that consumes variables needs its own declaration.
The secrets file uses standard .env syntax, one variable per line:
Values that contain whitespace, quotes, $, #, or newlines are emitted quoted so they survive sourcing and dotenv parsing unchanged. Multi-line values such as PEM keys work as-is:
The set -a && . /run/secrets/.env && set +a pattern loads every variable from the file into the shell environment for that RUN step, including quoted and multi-line values. The secret file is not persisted in the final image.
How you consume the file depends on your toolchain:
- Tools that expect environment variables (npm, pip, go): use the
set -apattern above to load them into the shell. - Frameworks that read
.envfiles directly: reference/run/secrets/.envin your build script instead of sourcing it. Dotenv parsers that support quoted multi-line values read the file unchanged. Use a current release: node dotenv 15 or newer, ruby dotenv 2.7 or newer, or python-dotenv. A value that combines a single quote with other shell metacharacters is written double-quoted, which a few dotenv parsers read differently than the shell does; single-quote it or split it if that matters.
Why secret mounts instead of ARG or ENV?#
Docker's ARG and ENV instructions are stored in the image layer history. Anyone with access to the image can extract them with docker history or docker inspect. Secret mounts avoid this problem: the file is available only during the RUN step and is never written to a layer.
Variable changes and the build cache#
UNKEY_SECRETS_ID is a hash of your project's variables. When you write id=${UNKEY_SECRETS_ID} on the mount line, BuildKit's cache key for that RUN includes the id, so any variable change produces a new id and a fresh execution of that step and everything after it in the same stage. Steps before the secret mount (base image pulls, dependency installs) keep caching normally.
Troubleshoot Dockerfile builds#
When a build fails, check the build logs in the Deployments tab. Common issues:
- Missing Dockerfile: Verify the Dockerfile path in your app settings.
- Dependency installation failures: Check that your base image includes the tools your Dockerfile needs.
- Context too large: If your build context includes large files (node_modules, data files), add a
.dockerignoreto exclude them.