Everything You Need to Know About Docker and Tilt
Docker and Tilt are two tools that often appear together in local development, and they solve different problems. Docker packages and runs your services as containers. Tilt watches your code and keeps those containers up to date while you work. Used together, they turn a folder of services into a running system that reacts to your edits.
This article explains what each tool does, how they fit together, where teams trip up, and how TDK CLI uses both.
What Docker does
Docker builds a container image from a description, usually a Dockerfile, and runs it as a container: an isolated process with its own filesystem, dependencies, and network settings. The key ideas are:
- Images are built in layers. Unchanged layers are cached, so rebuilds are fast when only your code changed.
- Containers are running instances of an image. They are disposable; you can stop and recreate them without losing the image.
-
Networks let containers reach each other by name. Services in the same network can call
http://orders:8080without knowing host ports. - Volumes persist data outside the container, such as database files.
Docker gives you consistent runtimes. A service that runs in its container on your laptop runs with the same OS packages, runtime version, and start command everywhere the image runs.
What Tilt does
Tilt is a development tool that runs on top of your container setup. You describe your services in a Tiltfile, and Tilt:
- builds your images and starts the containers,
- watches the source files you point it at,
- rebuilds or updates containers when those files change,
- shows logs and status for every resource in one dashboard.
The important feature is live update. For interpreted or compiled code, Tilt can copy changed files into a running container and restart the process, instead of rebuilding the whole image. Feedback that once took minutes can take seconds.
Tilt does not replace Docker. It orchestrates Docker on your machine, deciding what to build, when to rebuild, and how to keep containers current.
How they fit together
A simple mental model:
| Layer | Question it answers |
|---|---|
| Dockerfile | How do I build this service into an image? |
| Docker | How do I run that image as an isolated process with a network? |
| Tiltfile | What do I build, what do I watch, and how do I update it when code changes? |
| Tilt dashboard | Is everything running, and what did it log? |
You write the Dockerfile once per service. The Tiltfile tells Tilt where those Dockerfiles are and which files should trigger updates. Docker executes the work. Tilt decides when.
Writing a good Dockerfile
Because images are built in layers, order matters. Put instructions that change rarely first and instructions that change often last.
A typical pattern for a Node service:
- Start from a small, pinned base image.
- Copy only the dependency manifest (
package.jsonand its lockfile). - Install dependencies.
- Copy the source code.
- Set the start command.
Copying the source last means editing a file does not invalidate the dependency layer. Installing dependencies is usually the slowest step, so this ordering saves the most time.
Other habits that pay off:
- Pin base image versions so builds are reproducible.
-
Use a
.dockerignoreto keepnode_modules, build output, and secrets out of the build context. - Run as a non-root user where the base image allows it.
- Keep images small. Smaller images pull and start faster, which matters more when you rebuild often.
Writing a useful Tiltfile
A Tiltfile declares resources. For each service, you typically name the image, point Tilt at the Dockerfile, and list the paths whose changes should trigger an update. A good Tiltfile:
- Watches only the directories that matter. Watching the whole repository makes Tilt rebuild for unrelated changes.
-
Ignores generated and dependency folders, such as
node_modulesand build output, to avoid update loops. - Declares dependencies between resources, so a database starts before the API that needs it.
- Uses live update for code that can be copied in and restarted quickly.
Keep the Tiltfile readable. If a teammate cannot tell what each resource watches, the file will drift from reality.
Common pitfalls
- Watching too much. Tilt rebuilds on every change in a watched folder. Narrow the paths.
- Mixing build output and source. Generated files that Tilt watches can trigger an endless rebuild loop.
- Stale images. A cached layer hides a change when the Dockerfile or a copied file did not change as expected. Rebuild with a clean cache when debugging.
- Ignoring startup order. A running container is not a ready service. Check readiness, and declare dependencies explicitly.
- Secrets in the image. Anything copied into an image can be read from it. Pass secrets at runtime instead.
- Hand-editing generated files. If a tool generates your Dockerfile or Tiltfile, edit the source description, then regenerate. Hand edits get overwritten or drift.
Debugging with Docker and Tilt
When something fails, work outward from the smallest unit:
- Read the Tilt log for the failing resource. It shows build errors and runtime output in one place.
-
Check the container directly with
docker psanddocker logsto see whether the process started. - Rebuild the image by hand to confirm the Dockerfile itself works, separate from Tilt's update logic.
- Check networking from inside another container, to confirm service names and ports resolve.
- Check the watched paths if an update did not happen when you expected.
Most problems are either a Dockerfile that does not build, a process that exits at startup, or a path that Tilt is not watching.
How TDK CLI uses Docker and Tilt
TDK CLI builds its local stack on these two tools. Docker runs the containers. Tilt watches services and live-updates containers while you code. TDK CLI writes the configuration Tilt uses, so you do not hand-write it for each service.
The input is one service.json per service. From that file, TDK generates the Dockerfile, the Traefik routing configuration, and the Tiltfile. Running tdk up starts the stack, and tdk ui shows the status of running services in a terminal dashboard.
Because the generated files come from your service descriptions, they stay consistent with each other. TDK also records the files it generates and reports drift, so you can see when local configuration no longer matches the descriptions. Read what TDK writes for the full list.
TDK CLI is for local development. Production deployment stays with Helm, Argo CD, or Kustomize.
When to use Docker and Tilt alone
You do not need Tilt for every project. Docker alone is enough when you run a single service, rarely change code, or only need reproducible builds in CI. Tilt earns its place when you run several services and want changes to appear quickly without rebuilding the stack each time.
Summary
Docker gives each service a consistent, isolated runtime. Tilt keeps those runtimes current as you edit code. Write small, cache-friendly Dockerfiles, keep Tiltfiles narrow and readable, and debug from the smallest unit outward. TDK CLI generates both from one service.json per service, so the local stack stays consistent with the descriptions you wrote.
Top comments (0)