Custom Build Scripts
docker build is the common way to build container images, but there are others.
Tilt supports these other tools with the function
custom_build calls require:
A name of the image to build (as a ref, e.g.
A command to run (e.g.
bazel build //frontend:imageor
Files to watch (e.g.
['frontend', 'util', 'data.txt']). When a dependency changes, Tilt starts an update to build the image then apply the YAML.
There are a couple different image-building patterns.
Custom Docker Builds
Suppose you have a script that wraps
docker build, but adds some application-specific abstractions.
Here’s a simple example that invokes
docker build to build an image named
frontend from the directory
custom_build( 'frontend', 'docker build -t $EXPECTED_REF frontend', ['./frontend'], )
Tilt will run this command to build the image, verify that the image is in the Docker image store, then push the image to the appropriate image registry.
You can also use this pattern to use
docker flags that the
function doesn’t support.
Jib, Bazel, or any other builder that interoperates with Docker
Many tools can create Docker images, then write them to the local Docker image store.
For example, Jib has plugins that integrate with your existing Java tooling and create Java-based images.
custom_build( 'example-java-image', './gradlew jibDockerBuild --image $EXPECTED_REF', deps=['src'])
See the tutorial on how to use
custom_build to build images with
Buildah (or any image builder indepdendent of Docker)
Buildah is an independent Docker image builder.
Buildah has its own API and own image store. A
custom_build() call needs to
both build and push the image.
custom_build( 'frontend', 'buildah bud -t $EXPECTED_REF frontend && buildah push $EXPECTED_REF $EXPECTED_REF', ['./frontend'], skips_local_docker=True, )
skips_local_docker parameter indicates that we don’t expect the image to
ever show up in the local Docker image store. Tilt shouldn’t try to verify the
There are a couple of caveats you should be aware of with
buildah (and similar builders):
They often require privileged access. You may need to run Tilt with
sudoor inside an appropriate sandbox
If you’re using Tilt to push to an insecure registry, you will need to configure your builder for that registry. For example, to use
buildahwith Microk8s, you need to add
How to Write Your Own
All the commands above contain
$EXPECTED_REF. What is that?
Tilt always deploys with a digest, not a bare ref. (Instead of
gcr.io/company-name/frontend, Tilt injects
gcr.io/company-name/frontend:tilt-ffd9c2013b5bf5d4). Before explaining why (at the bottom of this document), let’s describe what this means for your Tiltfile and build script.
Because different build tools have different ergonomics, Tilt supports two modes:
- one-time tags via $EXPECTED_REF
- temporary refs
This mode is easy if your tool takes the destination of the image as an argument (e.g.
- Tiltfile sets a custom build command (e.g.
custom_build(..., 'docker build -t $EXPECTED_REF frontend')).
- Tilt sets a one-time ref as environment variable before executing the custom build command. (e.g.
- The custom build command builds the image and pushes to that ref (e.g. by reading
Other tools want to have an image ref hard-coded in configuration. They’ll build and push to the same place each time. Instead of having to change your tool, tell Tilt what tag the build image will have with
custom_build(..., tag='frontend:s2i'). After Tilt runs your build command, it will find this image and retag and push it with a unique ID.
Live Update and Other Features
docker_build supports other options. The most impactful is Live Update, which lets you update code in Kubernetes without doing a full image build.
custom_build supports this as well, using the same syntax.
custom_build supports most other options of
docker_build, and a few specific to non-Docker container builders. If you find an option you think should exist but doesn’t, let us know in the
#tilt channel in Kubernetes Slack.
Adjust File Watching with
While most of the points in our Debugging File Changes guide hold true for
ignore parameter (which adjusts the set of files watched for a given build) works a bit differently, and is worth discussing briefly.
ignore parameter takes a pattern or list of patterns (following
.dockerignore syntax; files matching any of these patterns will not trigger a build.
Of note, these patterns are evaluated relative to each
dep. E.g. given the following call:
custom_build( 'image-foo', 'docker build -t $EXPECTED_REF .', deps=['dep1', 'dep2'], ignores=['baz'] )
Tilt will ignore
Why Tilt uses One-Time Tags
This section describes for the curious why Tilt uses tags the way it does, instead of using a fixed reference. Kubernetes (and the OCI model generally) support multiple ways to reference an image:
gcr.io/company-name/frontend:username-devel. These tags can change as you upload new images to the same tag.
gcr.io/company-name/frontend. This is shorthand for the tag
latest, and changes even more frequently.
gcr.io/company-name/frontend:tilt-ffd9c2013b5bf5d4. The unique bit may be a Nonce or a digest of the contents. These don’t change once set (though technically they’re not write-protected).
Tilt only deploys One-Time references. Instead of pushing to
gcr.io/company-name/frontend and leaving the YAML as-is, Tilt retags the image and rewrites the container spec. This makes the Tilt experience more reliable. Deploying with a Tagged reference creates a race condition. Pods created at different times from the same definition may end up running different code as the reference is overwritten.
For a complete listing of all the
custom_build parameters, see API reference.