When you think about Docker, what comes to mind? Is it containerized apps for production? Maybe isolated environments for testing? But what if I told you Docker could also be your secret weapon for developer productivity? π©
Using Docker images for development environments ensures consistency, eliminates "it works on my machine" problems, and makes onboarding new developers a breeze. But building and managing these Docker imagesβespecially in a monorepoβcan get messy fast.
Enter Pants, a build tool designed to manage dependencies, optimize builds, and save you countless hours of waiting for Docker builds to finish. While Pants doesnβt directly create development environments, it does the heavy lifting of creating smart, optimized Docker images that are perfect for tools like Dev Containers, or even plain Docker CLI.
Hereβs how I made my Docker workflow smarter, faster, and a whole lot less frustrating with Pants.
Why development environments with Docker are hard
Docker is supposed to simplify development, right? Just run a container, and youβve got a fully isolated, consistent environment! But reality hits hard when you're working in a monorepo:
The struggles are real
Inconsistent tooling: One developer uses Node.js 16, another uses Node.js 20, and a third swears by Go 1.21. Suddenly, your βconsistentβ environment isnβt so consistent anymore.
Messy dependencies: Your Svelte frontend and Go backend both depend on a shared base image. Updating one image triggers a cascade of rebuilds, and if you donβt manage dependencies carefully, youβll end up with broken builds.
Painfully slow builds: Docker doesnβt automatically know which layers have changed, so you might find yourself rebuilding everything from scratchβeven for minor tweaks.
Dev environment chaos: Whether youβre using Dev Containers, Gitpod, or just Docker CLI, keeping all these tools in sync with your latest Docker images can feel like juggling flaming torches.
The Lightbulb moment: Pants to the rescue
I needed a better way to manage Docker builds. Enter Pants: a modern build tool designed for monorepos that understands dependencies, optimizes builds, and integrates seamlessly with Git.
Hereβs why Pants is my new best friend:
- Smart Dependency Management: Pants tracks dependencies between Docker images, so if one base image changes, only the affected images are rebuilt.
- Change Detection: Pants uses Git to detect file changes, rebuilding only whatβs necessary.
- Parallel Builds: It builds multiple images simultaneously wherever possible, saving loads of time.
- Flexible Integration: The Docker images you create with Pants work seamlessly with Dev Containers, Gitpod, or plain Docker setups.
Think of Pants as your Docker image orchestrator, ensuring everything builds in the right order, at the right time.
The Setup: Svelte + Go in a Monorepo
To demonstrate how Pants works, letβs take a monorepo with two projects:
- example-svelte-project: A frontend app built with Svelte and Vite.
- example-go-project: A backend API written in Go with some batch processing workers.
Directory structure
Hereβs what the monorepo looks like:
.
βββ example-svelte-project
β βββ docker
β β βββ common
β β β βββ ubuntu
β β β β βββ Dockerfile
β β β βββ mise
β β β β βββ Dockerfile
β β βββ frontend
β β β βββ vite
β β β βββ Dockerfile
βββ example-go-project
βββ docker
βββ common
β βββ ubuntu
β β βββ Dockerfile
β βββ go
β β βββ Dockerfile
βββ backend
β βββ api
β β βββ Dockerfile
β βββ batch
β β βββ Dockerfile
Step 1: Installing Pants
First, we need to install Pants. I used Ubuntu 24.04 as my base environment.
Installation Steps
- Install dependencies:
sudo apt update
sudo apt install -y ca-certificates curl unzip python3-dev build-essential
snap install go --classic --channel=1.22/stable
- Install Pants:
curl --proto '=https' --tlsv1.2 -sSfL 'https://static.pantsbuild.org/setup/get-pants.sh' | bash
echo 'export PATH=~/.local/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
- Verify installation:
pants --version
Step 2: Defining Docker Builds with Pants
Example 1: The Base Image (ubuntu/Dockerfile
)
This lightweight image serves as the foundation for all other images:
FROM ubuntu:24.04 AS base
ARG USER_NAME
RUN apt update && apt install -y sudo curl
RUN useradd -m $USER_NAME
Define it in example-svelte-project/docker/common/ubuntu/BUILD
:
docker_image(
name="dev-common-ubuntu",
source="Dockerfile",
image_tags=["dev-common-ubuntu"],
target_stage="base",
extra_build_args={"USER_NAME": "developer"},
)
Build it with Pants:
pants package example-svelte-project/docker/common/ubuntu:dev-common-ubuntu
Example 2: The Dev Tooling Image (mise/Dockerfile
)
This image includes tools like Node.js and Go for development:
FROM dev-common-ubuntu AS dev
RUN curl -fsSL https://get.mise-lang.sh | bash
Define it in example-svelte-project/docker/common/mise/BUILD
:
docker_image(
name="dev-common-mise",
source="Dockerfile",
dependencies=[":dev-common-ubuntu"],
image_tags=["dev-common-mise"],
)
Build it with Pants:
pants package example-svelte-project/docker/common/mise:dev-common-mise
Example 3: Svelte Frontend (vite/Dockerfile
)
This container runs the Svelte app:
FROM dev-common-mise AS dev
WORKDIR /app
COPY . .
RUN npm install && npm run build
Define it in example-svelte-project/docker/frontend/vite/BUILD
:
docker_image(
name="dev-frontend-vite",
source="Dockerfile",
dependencies=[":dev-common-mise"],
image_tags=["dev-frontend-vite"],
)
Step 3: Using Docker Images for Development
Now that weβve built the Docker images with Pants, itβs time to use them in our development workflows.
1. Dev Containers (VS Code)
Define a .devcontainer/devcontainer.json
file to use the Docker image as your development environment:
{
"image": "dev-common-mise:latest",
"extensions": [
"dbaeumer.vscode-eslint",
"golang.go"
]
}
2. Gitpod
In your .gitpod.yml
file:
image: dev-common-mise:latest
tasks:
- init: npm install
- command: npm run dev
3. Local Docker Environment
Run the Docker image locally:
docker run -it dev-common-mise:latest bash
Step 4: Optimizing the Workflow
Parallel Builds
Build multiple images at the same time:
pants package ::
Change Detection
Rebuild only whatβs changed:
pants --changed-since='HEAD' --changed-dependents=transitive package
Why this approach works
- Consistency: Developers work in identical environments using the same Docker images.
- Speed: Pants optimizes builds with change detection and parallelism.
- Flexibility: Use the same images for Dev Containers, Gitpod, or plain Docker CLI.
- Scalability: Shared base images reduce duplication and simplify maintenance.
Example repository
Siddhant-K-code / example-pants
A sample repository demonstrating the usage of the Pants build system
example-pants
A sample repository demonstrating the usage of the Pants build system.
Repository structure
.
βββ example-go-project
β βββ app
β β βββ backend
β β βββ frontend
β βββ docker
β βββ backend
β β βββ api
β β β βββ BUILD
β β β βββ Dockerfile
β β β βββ extensions.json
β β βββ batch
β β βββ BUILD
β β βββ Dockerfile
β β βββ extensions.json
β βββ common
β β βββ mise
β β β βββ BUILD
β β β βββ Dockerfile
β β βββ rust
β β β βββ BUILD
β β β βββ Dockerfile
β β βββ ubuntu
β β βββ BUILD
β β βββ Dockerfile
β β βββ Tiltfile
β βββ frontend
β βββ vite
β βββ BUILD
β βββ Dockerfile
β βββ extensions.json
βββ example-svelte-project
β βββ app
β β βββ backend
β β βββ frontend
β βββ docker
β βββ backend
β¦For more tips and insights, follow me on Twitter @Siddhant_K_code and stay updated with the latest & detailed tech content like this.
Top comments (0)