Introduction to build policies
Build policies let you validate the inputs to your Docker builds before they run. This tutorial walks you through creating your first policy, teaching the Rego basics you need along the way.
What you'll learn
By the end of this tutorial, you'll understand:
- How to create and organize policy files
- Basic Rego syntax and patterns
- How to write policies that validate URLs, checksums, and images
- How policies evaluate during builds
Prerequisites
- Buildx version 0.31 or later
- Basic familiarity with Dockerfiles and building images
How policies work
When you build an image, Buildx resolves all the inputs your
Dockerfile references: base images from FROM instructions, files
from ADD or COPY or build contexts, and Git repositories. Before
running the build, Buildx evaluates your policies against these
inputs. If any input violates a policy, the build fails before any
instructions execute.
Policies are written in Rego, a declarative language designed for expressing rules and constraints. You don't need to know Rego to get started - this tutorial teaches you what you need.
Create your first policy
Create a new directory for this tutorial and add a Dockerfile:
$ mkdir policy-tutorial
$ cd policy-tutorial
Create a Dockerfile that downloads a file with ADD:
FROM scratch
ADD https://example.com/index.html /index.htmlNow create a policy file. Policies use the .rego extension and live alongside
your Dockerfile. Create Dockerfile.rego:
package docker
default allow := false
allow if input.local
allow if {
input.http.host == "example.com"
}
decision := {"allow": allow}Save this file as Dockerfile.rego in the same directory as your Dockerfile.
Let's break down what this policy does:
package docker- All build policies must start with this package declarationdefault allow := false- This example uses a deny-by-default rule: if inputs do not match anallowrule, the policy check failsallow if input.local- The first rule allows any local files (your build context)allow if { input.http.host == "example.com" }- The second rule allows HTTP downloads fromexample.comdecision := {"allow": allow}- The final decision object tells Buildx whether to allow or deny the input
This policy says: "Only allow local files and HTTP downloads from
example.com". Rego evaluates all the policy rules to figure out the return
value for the decision variable, for each build input. The evaluations happen
in parallel and on-demand; the order of the policy rules has no significance.
About input.local
You'll see allow if input.local in nearly every policy. This rule allows
local file access, which includes your build context (typically, the .
directory) and importantly, the Dockerfile itself. Without this rule, Buildx
can't read your Dockerfile to start the build.
Even builds that don't reference any files from the build context often need
input.local because the Dockerfile is a local file. The policy evaluates
before the build starts, and denying local access means denying access to the
Dockerfile.
In rare cases, you might want stricter local file policies - for example, in CI builds where the build context uses a Git URL as a context directly. In these cases, you may want to deny local sources to prevent untracked files or changes from making their way into your build.
Automatic policy loading
Buildx automatically loads policies that match your Dockerfile name. When you
build with Dockerfile, Buildx looks for Dockerfile.rego in the same
directory. For a file named app.Dockerfile, it looks for
app.Dockerfile.rego.
This automatic loading means you don't need any command-line flags in most cases - create the policy file and build.
The policy file must be in the same directory as the Dockerfile. If Buildx
can't find a matching policy, the build proceeds without policy evaluation
(unless you use --policy strict=true).
For more control over policy loading, see the Usage guide.
Run a build with your policy
Build the image with policy evaluation enabled:
$ docker build .
The build succeeds because the URL in your Dockerfile matches the policy. Now try changing the URL in your Dockerfile to something else:
FROM scratch
ADD https://api.github.com/users/octocat /user.jsonBuild again:
$ docker build .
This time the build fails with a policy violation. The api.github.com
hostname doesn't match the rule in your policy, so Buildx rejects it before
running any build steps.
Debugging policy failures
If your build fails with a policy violation, use --progress=plain to see
exactly what went wrong:
$ docker buildx build --progress=plain .
This shows all policy checks, the input data for each source, and allow/deny decisions. For complete debugging guidance, see Debugging.
Add helpful error messages
When a policy denies an input, users see a generic error message. You can provide custom messages that explain why the build was denied:
package docker
default allow := false
allow if input.local
allow if {
input.http.host == "example.com"
input.http.schema == "https"
}
deny_msg contains msg if {
not allow
input.http
msg := "only HTTPS downloads from example.com are allowed"
}
decision := {"allow": allow, "deny_msg": deny_msg}Now when a build is denied, users see your custom message explaining what went wrong:
$ docker buildx build .
Policy: only HTTPS downloads from example.com are allowed
ERROR: failed to build: ... source not allowed by policy
The deny_msg rule uses contains to add messages to a set. You can add
multiple deny messages for different failure conditions to help users understand
exactly what needs to change.
Understand Rego rules
Rego policies are built from rules. A rule defines when something is allowed. The basic pattern is:
allow if {
condition_one
condition_two
condition_three
}All conditions must be true for the rule to match. Think of it as an AND operation - the URL must match AND the checksum must match AND any other conditions you specify.
You can have multiple allow rules in one policy. If any rule matches, the
input is allowed:
# Allow downloads from example.com
allow if {
input.http.host == "example.com"
}
# Also allow downloads from api.github.com
allow if {
input.http.host == "api.github.com"
}This works like OR - the input can match the first rule OR the second rule.
Access input fields
The input object gives you access to information about build inputs. The
structure depends on the input type:
input.http- Files downloaded withADD https://...input.image- Container images fromFROMorCOPY --frominput.git- Git repositories fromADD git://...or build contextinput.local- Local file context
Refer to the Input reference for all available input fields.
For HTTP downloads, you can access:
| Key | Description | Example |
|---|---|---|
input.http.url | The full URL | https://example.com/index.html |
input.http.schema | The protocol (HTTP/HTTPS) | https |
input.http.host | The hostname | example.com |
input.http.path | The URL path, including parameters | /index.html |
Update your policy to require HTTPS:
package docker
default allow := false
allow if {
input.http.host == "example.com"
input.http.schema == "https"
}
decision := {"allow": allow}Now the policy requires both the hostname to be example.com and the protocol
to be HTTPS. HTTP URLs (without TLS) would fail the policy check.
Pattern matching and strings
Rego provides built-in functions for pattern matching. Use startswith() to
match URL prefixes:
allow if {
startswith(input.http.url, "https://example.com/")
}This allows any URL that starts with https://example.com/.
Use regex.match() for complex patterns:
allow if {
regex.match(`^https://example\.com/.+\.json$`, input.http.url)
}This matches URLs that:
- Start with
https://example.com/ - End with
.json - Have at least one character between the domain and extension
Policy file location
Policy files live adjacent to the Dockerfile they validate, using the naming
pattern <dockerfile-name>.rego:
project/
├── Dockerfile # Main Dockerfile
├── Dockerfile.rego # Policy for Dockerfile
├── lint.Dockerfile # Linting Dockerfile
└── lint.Dockerfile.rego # Policy for lint.DockerfileWhen you build, Buildx automatically loads the corresponding policy file:
$ docker buildx build -f Dockerfile . # Loads Dockerfile.rego
$ docker buildx build -f lint.Dockerfile . # Loads lint.Dockerfile.rego
Next steps
You now understand how to write basic build policies for HTTP resources. To continue learning:
- Apply and test policies: Using build policies
- Learn Image validation to validate container images
from
FROMinstructions - Learn Git validation to validate Git repositories used in builds
- See Example policies for copy-paste-ready policies covering common scenarios
- Write unit tests for your policies: Test build policies
- Debug policy failures: Debugging
- Read the Input reference for all available input fields
- Check the Built-in functions for signature verification, attestations, and other security checks