Dockerfile and Containerization Strategy for macro-inc/macro: A Complete Guide
The macro-inc/macro repository employs a layered, multi-stage Docker strategy that isolates Rust compilation from runtime using Debian Trixie-slim images, alongside service-specific Dockerfiles and a composite development container orchestrated via Docker Compose.
The macro-inc/macro monorepo implements a sophisticated containerization strategy designed for microservice isolation and reproducible builds. This architecture leverages multi-stage Dockerfiles to minimize production image sizes while providing comprehensive Docker Compose orchestration for local development and CI/CD pipelines.
Multi-Stage Production Build Architecture
The Rust Chef and Builder Stages
The primary production pipeline resides in docker/Dockerfile, implementing a three-stage build process. The first stage utilizes a Rust "chef" image (FROM rust:latest AS chef) to install the Cargo workspace and cache dependencies. The subsequent builder stage (FROM chef AS builder) compiles every service using cargo build --release, creating optimized binaries while maintaining build artifacts in isolated layers.
The Runtime Runner Stage
The final stage switches to a lightweight Debian Trixie-slim base (FROM debian:trixie-slim AS runner), copying only the compiled binaries and essential runtime libraries from the builder. This approach yields minimal production images of approximately 100 MB, significantly reducing the attack surface and deployment time compared to standard Rust images.
Service-Specific Containerization Patterns
WebSocket Service Container
Individual microservices maintain dedicated Dockerfiles under docker/ for specialized runtime requirements. The WebSocket Service defined in docker/websocket-service.Dockerfile utilizes the oven/bun base image for Node.js runtime capabilities, copying the pre-compiled binary into an optimized container designed for real-time communication workloads.
Sync Service Multi-Runtime Build
The Sync Service (docker/sync-service.Dockerfile) demonstrates complex multi-runtime containerization by combining Rust builder stages with Node 22-slim environments. This configuration handles frontend asset compilation alongside backend binary generation, ensuring both Rust and JavaScript dependencies resolve correctly within isolated build contexts.
Search Processing Service Pipeline
For the search infrastructure, docker/Dockerfile.search_processing_service extends the standard pattern with four distinct stages: chef → planner → builder → runner. This granular separation optimizes caching for the OpenSearch integration pipeline while maintaining the minimal Debian-based runtime environment.
Development Environment and Orchestration
Composite Development Image (Dockerfile.dev)
Local development utilizes docker/Dockerfile.dev to create a unified container bundling all services. This sequential layer approach (FROM services_bundle AS authentication_service, etc.) enables rapid iteration via docker compose up, launching the entire stack from a single image while preserving service isolation in the build steps.
Docker Compose Orchestration
The docker/docker-compose.yml file orchestrates all microservices alongside supporting infrastructure including PostgreSQL, Redis, and OpenSearch. Each service references its respective Dockerfile, exposing required ports and establishing network connectivity for end-to-end local development.
Infrastructure Stacks
Additional containerization configurations reside under infra/, including infra/stacks/fusionauth-instance/docker-compose.yml for authentication testing and infra/local/opensearch/Dockerfile for search node provisioning. These self-contained stacks embody the repository's "in-repo, self-contained" philosophy for dependency management.
Build Automation and Tooling
Just Task Runner Integration
The project employs the just task runner to standardize Docker operations across environments. The just wrapper commands synchronize SQLx query caches before compilation and manage multi-service builds:
# Build the production image
just build
# Build only the sync service
just build sync_service
# Prepare database schemas before Docker build
just prepare_db
CI/CD Container Workflows
For continuous integration, the multi-stage Dockerfiles support standardized build patterns. The following commands demonstrate production and single-service builds:
# Build and tag the production image
docker build -f docker/Dockerfile -t macro:latest .
# Build a specific service image
docker build -f docker/sync-service.Dockerfile -t macro/sync-service .
Local Development Commands
To run the full stack locally, execute the following sequence:
# Pull supporting infrastructure (Postgres, Redis, OpenSearch)
docker compose -f docker/docker-compose.yml pull
# Build the composite development image
docker build -f docker/Dockerfile.dev -t macro/dev .
# Start all services
docker compose -f docker/docker-compose.yml up -d
Summary
- Multi-stage isolation: The
docker/Dockerfileseparates Rust compilation (chef/builder) from runtime (Debian Trixie-slim), producing ~100 MB production images. - Service-specific optimization: Individual Dockerfiles like
websocket-service.Dockerfileandsync-service.Dockerfileaccommodate multi-runtime requirements (Rust + Bun/Node). - Unified development:
Dockerfile.devbundles all services into a single image for rapiddocker compose upworkflows. - Infrastructure as code: The
infra/directory contains self-contained stacks for OpenSearch and FusionAuth, enabling complete local testing environments. - Build automation: The just task runner integrates SQLx cache preparation with Docker builds for reproducible CI/CD pipelines.
Frequently Asked Questions
What containerization strategy does macro-inc/macro use?
The repository implements a multi-stage Docker build strategy that separates compilation from runtime. Production images are built via docker/Dockerfile using Rust chef/builder stages followed by a minimal Debian Trixie-slim runner, while development uses Dockerfile.dev to bundle all services into a single composite image orchestrated by Docker Compose.
How does the multi-stage Dockerfile reduce image size?
By utilizing distinct builder and runner stages in docker/Dockerfile, the build process discards compilation tools and intermediate artifacts after creating the release binaries. Only the compiled binaries and essential runtime libraries are copied to the final Debian Trixie-slim stage, reducing the final image size to approximately 100 MB compared to standard Rust images that include the full toolchain.
Can I run a single service without building the entire monorepo?
Yes. The repository provides service-specific Dockerfiles such as docker/sync-service.Dockerfile and docker/websocket-service.Dockerfile that can be built independently. Use docker build -f docker/sync-service.Dockerfile -t macro/sync-service . to build only the sync service, then run it attached to the shared Docker Compose network via docker run -d --network macro_default macro/sync-service.
What is the purpose of Dockerfile.dev versus the production Dockerfile?
docker/Dockerfile creates minimal, production-ready images containing only compiled binaries for deployment. In contrast, docker/Dockerfile.dev generates a composite development image that includes all services and development dependencies, allowing developers to launch the entire stack with a single docker compose up command for rapid local iteration and debugging.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →