Dexfile is a BuildKit frontend built on top of Dockerfile. Every valid Dockerfile is also a valid Dexfile, but Dexfile extends the syntax with powerful new instructions for conditional logic, modularity, and advanced build workflows.
Like Docker, Dexfile allows you to define how images are built through a text-based file. It translates source code into build artifacts in an efficient, expressive, and repeatable way—ideal for modern CI/CD pipelines and platform operators.
-
Automatic garbage collection
-
Concurrent dependency resolution
-
Efficient instruction caching
-
Build cache import/export
-
Multiple output formats
-
Execution without root privileges
-
Integration for PaaS operators – enabling one-click deploys
-
Simplified CI/CD – replacing complex build scripts and config files
-
Selective rebuilds – build only what changed to reduce time and costs
-
Improved reliability – clearer separation of development vs. deployment
Dexfile does not require a separate installation. It is distributed as a BuildKit frontend, so you only need to ensure your environment has one of the following:
-
Install Docker.
-
Ensure your Docker version supports BuildKit (Docker 18.09+).
-
Enable BuildKit:
export DOCKER_BUILDKIT=1Now, docker build can consume Dexfiles.
-
Install BuildKit.
-
Start the BuildKit daemon (either locally or via container):
buildkitd
-
Use
buildctlto build with Dexfile:buildctl build \ --frontend=gateway.v0 \ --local context=. \ --local dexfile=.
To start using Dexfile in your project:
-
At the top of your
Dexfile, declare the Dexfile frontend:# syntax=dexnore/dexfile:latestThis will let the docker tools to interpret Dexfile content
-
With Docker:
docker buildx build -f Dexfile -t myapp:latest .With BuildKit directly:
buildctl build \
--frontend=gateway.v0 \
--local context=. \
--local dexfile=. \
--output type=docker,name=myapp:latestPlace a .dexnore file in your project root to exclude unnecessary files from the build context (similar to .dockerignore):
node_modules
*.log
secrets/Dexfile supports every dockerfile instruction and it’s flags until and unless specified explicitly. every Dockerfile is a valid Dexfile until it has valid syntax directive pointing to Dexfile image, but not vice versa.
Dexfile supports the following commands
| Instruction | Description |
|---|---|
ADD |
Add local or remote files and directories. |
ARG |
Use build-time variables. |
CMD |
Specify default commands. |
COPY |
Copy files and directories. |
ENTRYPOINT |
Specify default executable. |
ENV |
Set environment variables. |
EXPOSE |
Describe which ports your application is listening on. |
FROM |
Create a new build stage from a base image. |
HEALTHCHECK |
Check a container's health on startup. |
LABEL |
Add metadata to an image. |
MAINTAINER |
Specify the author of an image. |
ONBUILD |
Specify instructions for when the image is used in a build. |
RUN |
Execute build commands. |
SHELL |
Set the default shell of an image. |
STOPSIGNAL |
Specify the system call signal for exiting a container. |
USER |
Set user and group ID. |
VOLUME |
Create volume mounts. |
WORKDIR |
Change working directory. |
In Addition Dexfile introduces the following new commands
| Instruction | Description |
|---|---|
IF/ELSE |
perform varying commands depending on the outcome of one or more conditions |
IMPORT |
import external dockerfiles and dexfiles stages for reusability |
FUNC |
reusable block of instructions within the file |
FOR |
loop over the set of result items |
PROC |
start a process in the ephemeral container |
BUILD |
build the target stage from scratch and emit output |
EXEC |
Incorporate the LLB result produced by EXEC into the active Dexfile build state. |
The IF clause can perform varying commands depending on the outcome of one or more conditions. The expression passed as part of <condition> is evaluated by running it in the build environment. If the exit code of the expression is zero, then the block of that condition is executed. Otherwise, the control continues to the next ELSE IF condition (if any), or if no condition returns a non-zero exit code, the control continues to executing the <else-block>, if one is provided.
IF [<condition-options>...] [ RUN | EXEC | PROC | BUILD ] <condition>
<if-block>
ENDIFIF [<condition-options>...] [ RUN | EXEC | PROC | BUILD ] <condition>
<if-block>
ELSE
<else-block>
ENDIFIF [<condition-options>...] [ RUN | EXEC | PROC | BUILD ] <condition>
<if-block>
ELSE IF [<condition-options>...] [ RUN | EXEC | PROC | BUILD ] <condition>
<else-if-block>
...
ELSE
<else-block>
ENDIFImportant
Changes to the filesystem in expressions are not preserved. If a file is created as part of a IF/ELSE expression, then that file will not be present in the build environment for any subsequent commands.
A very common pattern is to use the POSIX shell [ ... ] conditions. For example the following marks port 8080 as exposed if the file ./foo exists.
IF RUN [ -f ./foo ]
EXPOSE 8080
ENDIFThe IF or the ELSE conditions emit STDOUT and STDERR ARGs within the scope of the condition, which can be used to take farther actions
| ARG | Description |
|---|---|
STDOUT |
The standard output of the condition |
STDERR |
The standard error of the condition |
IF RUN echo "hello world!"
RUN echo "${STDOUT}" # "hello world!"
ENDIF| FLAG | Description | Default |
|---|---|---|
timeout |
timeout for evaluating the condition | 10m |
—timeout flag is used to adjust the timeout of the condition. after exceeding the given timeout, the condition immediatly exits with error status code
IF RUN sleep 900 && echo "hello world!" # this condition fails due to timeout
RUN echo "succeed"
ELSE # this conditional block executes
RUN echo "failed"
ENDIFThe IMPORT instruction allows you to bring in stages from external Dockerfiles or Dexfiles. This enables composability and reuse, letting you split common build stages into separate files and reuse them across projects.
This is particularly useful in:
-
Monorepos – share build logic across services.
-
Polyrepos – reuse common base images or build stages.
-
Nested project structures – import from deep subdirectories when needed.
IMPORT [<import_options>...] <dexfile-ref> AS <alias>-
<dexfile-ref>defines the source (local, remote, Git, registry, etc.). -
<alias>names the imported stage so it can be referenced later.
| Type | Example Usage | Description |
|---|---|---|
local: |
local:context |
Import from a local folder or file. |
docker-image:// |
docker-image://ubuntu:22.04 |
Import from a Docker/OCI image. |
oci-layout:// |
oci-layout://images/app |
Import from an OCI image layout directory. |
inputs: |
inputs:buildctx |
Import from a named BuildKit input context. |
git: |
git:https://github.com/org/repo.git#main:docker |
Import from Git repo branch/tag/subdir. |
https:// |
https://raw.githubusercontent.com/org/repo/Dexfile |
Import a Dexfile/Dockerfile over HTTP(S). |
all the options are optional
| FLAG | Description | type |
|---|---|---|
platform |
Target platform for the import. Defaults to current build platform. | string |
target |
Target stage to import (defaults to the last stage in the file). | string |
file |
Path to a Dexfile or Dockerfile (if omitted, <dexfile-ref> is treated as a blob). |
string |
opt |
Build arguments (KEY=VALUE) passed to the imported stage. Can be repeated. |
array of string |
IMPORT --platform=linux/arm64 \
--target=release \
--file=./Dexfile \
--opt MY_ARG=value \
local:context AS ctxIn this example:
-
Import from the local context.
-
Restrict to
linux/arm64builds. -
Use the
releasestage from./Dexfile. -
Pass a custom build argument (
MY_ARG=value). -
Alias the import as
ctx.
The FUNC instruction defines a reusable function within a Dexfile. Functions allow you to group a set of instructions and reuse them across multiple stages, targets, or even within other functions.
In order to reference and execute a function, you may use the command FUNC CALL.
Unlike performing a BUILD release, functions inherit the build context and the build environment from the caller.
Functions create their own ARG scope, which is distinct from the caller. Any ARG that needs to be passed from the caller needs to be passed explicitly via FUNC --<build-arg-key>=<build-arg-value> CALL release.
FUNC [ <build-arg-key>=<build-arg-value>... ] <func_name>
<dexfile_commands>...
ENDFUNC-
<func_name>– the name of the function. -
[<arg_key>=<arg_value>...]– optional arguments defined for the function. -
<dexfile_commands>– the body of the function (any valid Dexfile instructions).
To invoke a function, use:
FUNC [--<arg_key>=<arg_value>...] CALL <func_name>Define a function:
FUNC --greet="world" hello-world
RUN echo "hello ${greet}!"
ENDFUNCCall the function with a different argument:
FUNC --greet="dexfile" CALL hello-world
# expands to: RUN echo "hello dexfile!"-
Functions promote reusability and modularity in Dexfiles.
-
Caller and callee environments share the same build context.
-
ARG values must be explicitly passed when calling a function.
-
Functions can be nested or composed inside other functions for advanced workflows.
The FOR clause iterates over the items resulting from an expression (<expression>). On each iteration, the value of <variable-name> is set to the current item, and the block of commands (<for-block>) is executed in the context of that variable, which is available as a build argument.
Important
Changes to the filesystem in expressions are not preserved. If a file is created as part of a FOR expression, then that file will not be present in the build environment for any subsequent commands.
| ARG | Description |
|---|---|
INDEX |
Current index of the iteration |
<vaiable-name> |
Value of the current item |
FOR [<options>...] <variable-name> IN [ RUN | EXEC | PROC ] <expression>
<for-block>
ENDFOR| FLAG | Description | Default |
|---|---|---|
regex |
Regex compiled against the expression output to generate the variable value | \n |
regex-action |
How to interpret the regex against the output (split or match) |
split |
timeout |
Timeout for evaluating the expression | 10m |
FOR file IN RUN ls
RUN gcc "${file}" -o "${file}.o" -c
ENDExplanation:
-
The
RUN lsexpression lists all files in the current directory. -
Each result is assigned to the variable
file. -
On each iteration,
gcccompiles that file into an object file (.o). -
After the loop completes, every file in the directory has been compiled.
The PROC instruction starts a process inside an ephemeral container created by CTR.
Any filesystem changes made by this process are discarded when the container exits.
This makes it ideal for testing or running commands without persisting changes.
Each PROC emits two ARGs: STDOUT and STDERR.
These can be referenced by subsequent instructions to trigger additional actions.
| ARG | Description |
|---|---|
STDOUT |
Standard output of the executed process |
STDERR |
Standard error output of the executed process |
PROC [ <proc_options>... ] <expression>| FLAG | Description | Default |
|---|---|---|
from |
Target container name to execute the process in | current container |
timeout |
Timeout for process execution | 10m |
CTR hello-world
CTR hello-dexfile
# execute process in `hello-dexfile` container
PROC echo "hello dexfile!"
# execute process in `hello-world` container
PROC --from="hello-world" echo "hello world!"
ENDCTR
ENDCTRThe BUILD instruction triggers Dexfile to invoke the build of a target stage referenced by <target-ref>.
Once a BUILD command is reached, no further instructions in the current stage are executed.
Unlike multi-stage COPY --from=..., the BUILD instruction rebuilds the referenced target from scratch, ignoring any prior build state or caching of that target. This ensures deterministic and isolated builds.
BUILD [ <build-arg-key>=<build-arg-value>... ] <target-ref>-
<target-ref>: The name of the stage or target to build. -
<build-arg-key>=<build-arg-value>: Optional build arguments to pass into the referenced target.
| FLAG | Description |
|---|---|
<build-arg-key>=<build-arg-value> |
Array of key-value pairs passed as build-time arguments for the target stage |
-
A
BUILDalways starts the referenced target from scratch. -
The current stage stops execution after a
BUILD. -
Useful for composing multiple targets or chaining dependent builds.
-
Acts like calling another stage’s build as a subroutine.
FROM alpine:latest as hello-world
RUN echo "hello world!"
FROM scratch
# Build the hello-world target from scratch
BUILD hello-worldIn this example:
-
The first stage (
hello-world) prints a message. -
The second stage (
scratch) invokesBUILD hello-world, rebuilding it independently.
The EXEC instruction is used for advanced scenarios where Dexfile’s built-in instructions are insufficient.
It executes an external command, expecting its stdout to emit BuildKit LLB (Low-Level Build) state in Protobuf format.
The emitted LLB is then merged into the current build state, effectively extending the build graph dynamically.
EXEC [ <run_options>... ] <expression>-
<expression>: The command or binary to execute. -
The command’s stdout must output a valid BuildKit LLB definition.
The EXEC instruction supports mount network security flags supported by RUN instruction.
FROM mycustomimage:latest
# print-proto executable stdout's the buildkit supported llb state
EXEC print-proto-
EXECallows custom integration of external tools with BuildKit. -
The emitted LLB is merged into the current build state.
-
If the command fails or emits invalid LLB, the build fails.
The meta stage is a lightweight, implicit initialization stage that runs before any standard build stages. Its purpose is to centralize global configuration, environment setup, and reusable build arguments, providing a deterministic foundation for all subsequent stages.
- Implicit Stage
- Any Dexfile instruction before the first
FROMis automatically part of the meta stage. - No explicit declaration is required; Dexfile recognizes these instructions and executes them in isolation.
- Any Dexfile instruction before the first
- Base Image
- The meta stage always uses
busybox:latestas its base image. - This keeps it lightweight and ensures deterministic behavior.
- The meta stage always uses
- Global Arguments
- All
ARGandENVinstructions defined in the meta stage become meta arguments. - Meta arguments are globally accessible to all subsequent stages, allowing consistent configuration.
- All
- Isolation and Determinism
- All filesystem changes and instructions in the meta stage are confined to that stage.
- Changes are only propagated to later stages if explicitly referenced via COPY --from=meta or similar mechanisms.
ARG VERSION=1.6.1
IF RUN [ -z "${VERSION}" ]
RUN echo "VERSION is required" >&2 && exit 1
ENDIF
FROM alpine:$VERSION