ci/docker_utils/README.md
FastLED provides two complementary Docker build systems:
Local development tool for cross-platform compilation with pre-cached dependencies.
This Docker-based build system enables fast, reproducible compilation of FastLED sketches across multiple platforms without installing platform-specific toolchains on your host machine.
The system uses Option 1: Bake-In Dependencies architecture:
--rm flag)fastled-platformio-{architecture}-{platform}-{hash}
Example: fastled-platformio-esp32-esp32s3-a1b2c3d4
The hash is automatically generated from:
espressif32)arduino)esp32-s3-devkitc-1)Cache invalidation is automatic: When platform config changes (e.g., ESP32 SDK link update in ci/boards.py), a new hash is generated and a new image is built.
# Build image for Arduino Uno
uv run python ci/build_docker_image_pio.py --platform uno
# Build image for ESP32-S3
uv run python ci/build_docker_image_pio.py --platform esp32s3 --framework arduino
# Build with custom platformio.ini
uv run python ci/build_docker_image_pio.py --platformio-ini ./custom-config.ini
# Force rebuild without cache
uv run python ci/build_docker_image_pio.py --platform uno --no-cache
Recommended: Use the bash compile command with --docker flag for automatic setup:
# Simple compilation with automatic Docker detection
bash compile esp32dev Blink --docker
# Compile with output directory (automatically mounted as volume)
bash compile esp32dev Blink --docker -o ./build_output
# Artifacts appear immediately in ./build_output on host
Advanced: Manual Docker commands for custom workflows:
The Docker image contains a snapshot of FastLED from GitHub. To compile your local code, rsync your repo and run the same compile command:
# The pattern: bash compile <platform> <example> --docker
# Host command: bash compile esp32dev Blink --docker
# Container command: bash compile esp32dev Blink (mirrors host, minus --docker flag)
# Compile with output directory
docker run --rm \
-v $(pwd):/host:ro \
-v ./build_output:/fastled/output:rw \
fastled-platformio-esp32-esp32dev-abc123 \
bash -c "rsync -a /host/ /fastled/ && bash compile esp32dev Blink"
# Compile without output directory (shorter example)
docker run --rm \
-v $(pwd):/host:ro \
fastled-platformio-avr-uno-abc123 \
bash -c "rsync -a /host/ /fastled/ && bash compile uno Blink"
# Interactive shell for debugging
docker run --rm -it \
-v $(pwd):/host:ro \
fastled-platformio-avr-uno-abc123 \
bash
# Inside container:
# rsync -a /host/ /fastled/
# bash compile uno Blink
NEW: When using the -o or --out flag with bash compile, the output directory is automatically mounted as a volume:
# Automatically mounts ./build_output as /fastled/output in container
bash compile esp32dev Blink --docker -o ./build_output
# Artifacts appear directly in ./build_output on host
# - firmware.bin
# - firmware.elf
# - firmware.factory.bin (ESP32 merged binary)
How it works:
/fastled/output:rw in containerdocker cpSecurity: Output directory must be relative to current directory or a subdirectory. Absolute paths outside the project are rejected.
For custom Docker usage, you can manually mount volumes:
-v $(pwd):/host:ro - Mount your FastLED repo as read-only
rsync -a /host/ /fastled/ inside container to update code-v ./build_output:/fastled/output:rw - Output directory for compiled binaries
When /fastled/output is mounted, the entrypoint automatically copies build artifacts after successful compilation:
*.bin - Binary files (ESP32, STM32, etc.)*.hex - Hex files (AVR platforms)*.elf - ELF files (debugging symbols)*.factory.bin - Merged binaries for ESP32 (includes bootloader + partitions + app)Key Insight: /fastled/ inside the container starts with a GitHub snapshot, then gets updated via rsync with your local code. The compile script works identically inside and outside the container.
Uses platform configuration from ci/boards.py:
uv run python ci/build_docker_image_pio.py --platform uno
uv run python ci/build_docker_image_pio.py --platform esp32s3 --framework arduino
Advantages:
Uses a custom platformio.ini file:
uv run python ci/build_docker_image_pio.py --platformio-ini ./custom-config.ini
Use cases:
Note: Hash-based naming is not available in Mode B (uses generic image name).
# List all FastLED PlatformIO images
docker images fastled-platformio-*
# Check image details
docker image inspect fastled-platformio-avr-uno-abc123
The prune_old_images.py script removes outdated images:
# Dry run (show what would be deleted)
uv run python ci/docker_utils/prune_old_images.py
# Actually delete old images (keeps newest for each platform)
uv run python ci/docker_utils/prune_old_images.py --force
# Remove images older than 30 days
uv run python ci/docker_utils/prune_old_images.py --days 30 --force
# Remove images for specific platform only
uv run python ci/docker_utils/prune_old_images.py --platform esp32s3 --force
# Remove ALL FastLED PlatformIO images (dangerous!)
uv run python ci/docker_utils/prune_old_images.py --all --force
Default behavior (without --days or --all):
# Remove specific image
docker rmi fastled-platformio-avr-uno-abc123
# Remove all FastLED images (use with caution!)
docker rmi $(docker images -q fastled-platformio-*)
# Remove unused Docker images (generic cleanup)
docker image prune -a --filter "until=720h" # Remove images older than 30 days
Input:
uno, esp32s3) → reads from ci/boards.pyProcess:
platformio.ini from board configurationOutput:
fastled-platformio-{architecture}-{platform}-{hash}~/.platformio/packages and ~/.platformio/platformsInput:
Process:
Output:
Key Insight: Platform configuration is baked into the image at build time. Runtime only needs source code and compilation command.
For ESP32 platforms, the system automatically supports merged binary generation:
What is a merged binary?
.factory.bin fileGeneration: PlatformIO automatically creates merged binaries at:
.pio/build/<env>/firmware.factory.bin
Access merged binaries:
Use output directory mount:
docker run --rm \
-v ./src:/fastled/src:ro \
-v ./examples:/fastled/examples:ro \
-v ./build_output:/fastled/output:rw \
fastled-platformio-esp32-esp32s3-abc123 \
pio run
Check ./build_output/firmware.factory.bin on host
The system supports all platforms defined in ci/boards.py:
uno, nano_every, leonardoattiny85, attiny88, attiny4313attiny1604, attiny1616 (megaAVR)esp32dev, esp32s2, esp32s3, esp32c3, esp32c6sam3x8e_due (SAM)stm32f103c8, stm32f103cb, stm32f103tb, stm32f411ce, stm32h747xi (STM32)teensy30, teensy40, teensy41 (Teensy)rp2040, rp2350 (RP2040)nrf52840_dk, xiaoblesense (nRF52)uno_r4_wifi (Renesas)apollo3_red (Apollo3)native (host compilation)To see all available platforms:
uv run python -c "from ci.boards import ALL; print([b.board_name for b in ALL])"
# Check if Docker is installed
docker --version
# Install Docker Desktop
# https://www.docker.com/products/docker-desktop
On Linux, you may need to add your user to the docker group:
sudo usermod -aG docker $USER
# Log out and back in for changes to take effect
# Try rebuilding without cache
uv run python ci/build_docker_image_pio.py --platform uno --no-cache
# Check base image exists
docker images fastled-platformio:latest
# Rebuild base image if needed
# (base image is automatically built by build_docker_image_pio.py)
# Run with interactive shell to debug
docker run --rm -it \
-v ./src:/fastled/src:ro \
-v ./examples:/fastled/examples:ro \
fastled-platformio-avr-uno-abc123 \
/bin/bash
# Inside container:
pio run --verbose
Ensure directory is created and has write permissions:
mkdir -p ./build_output
chmod 777 ./build_output # Or appropriate permissions
Check if output directory is mounted:
docker run --rm \
-v ./build_output:/fastled/output:rw \
fastled-platformio-avr-uno-abc123 \
ls -la /fastled/output
uv run python ci/build_docker_image_pio.py \
--platform uno \
--image-name my-custom-uno-image
# Some platforms support multiple frameworks
uv run python ci/build_docker_image_pio.py \
--platform esp32s3 \
--framework arduino
# Run interactive shell
docker run --rm -it \
fastled-platformio-avr-uno-abc123 \
/bin/bash
# Inside container:
ls -la ~/.platformio/packages # Pre-cached toolchains
ls -la ~/.platformio/platforms # Pre-cached platforms
# Ensure your sketch is in examples/ directory
# Then mount it and run:
docker run --rm \
-v ./src:/fastled/src:ro \
-v ./examples:/fastled/examples:ro \
fastled-platformio-avr-uno-abc123 \
pio run -e uno
We chose Option 1 over Option 2 (base image + runtime install) for:
Hash-based naming provides automatic cache invalidation:
Scenario: ESP32 SDK URL changes in ci/boards.py
Old approach:
fastled-platformio-esp32-esp32s3--no-cacheHash-based approach:
fastled-platformio-esp32-esp32s3-abc123 (stale SDK)fastled-platformio-esp32-esp32s3-def456 (new SDK)Benefits:
This Docker system is designed for local development, not for CI:
Why not CI?
ci/ci-compile.py infrastructureUse cases:
FastLED also provides pre-built Docker images on Docker Hub for CI/CD and production use.
# Pull and use pre-built images (no local build needed!)
bash compile uno Blink --docker
bash compile esp32s3 Blink --docker
bash compile teensy41 Blink --docker
These images are:
| Platform | Docker Image | Cached Boards |
|---|---|---|
| AVR | niteris/fastled-compiler-avr:latest | uno, leonardo, attiny85, attiny88, attiny4313, nano_every, attiny1604, attiny1616 |
| ESP32 (Original) | niteris/fastled-compiler-esp-32dev:latest | esp32dev |
| ESP32-S2 | niteris/fastled-compiler-esp-32s2:latest | esp32s2 |
| ESP32-S3 | niteris/fastled-compiler-esp-32s3:latest | esp32s3 |
| ESP8266 | niteris/fastled-compiler-esp-8266:latest | esp8266 |
| ESP32-C2 | niteris/fastled-compiler-esp-32c2:latest | esp32c2 |
| ESP32-C3 | niteris/fastled-compiler-esp-32c3:latest | esp32c3 |
| ESP32-C5 | niteris/fastled-compiler-esp-32c5:latest | esp32c5 |
| ESP32-C6 | niteris/fastled-compiler-esp-32c6:latest | esp32c6 |
| ESP32-H2 | niteris/fastled-compiler-esp-32h2:latest | esp32h2 |
| ESP32-P4 | niteris/fastled-compiler-esp-32p4:latest | esp32p4 |
| Teensy | niteris/fastled-compiler-teensy:latest | teensylc, teensy30, teensy31, teensy40, teensy41 |
| STM32 | niteris/fastled-compiler-stm32:latest | stm32f103c8, stm32f103cb, stm32f103tb, stm32f411ce, stm32h747xi |
| RP | niteris/fastled-compiler-rp:latest | rp2040, rp2350 |
| NRF52 | niteris/fastled-compiler-nrf52:latest | nrf52840_dk, adafruit_feather_nrf52840_sense, xiaoblesense |
| SAM | niteris/fastled-compiler-sam:latest | sam3x8e_due |
# Any AVR board compiles instantly using the same image
bash compile uno Blink --docker
bash compile attiny85 Blink --docker
bash compile nano_every Blink --docker
# All ESP32 variants use the same image
bash compile esp32dev Blink --docker
bash compile esp32s3 Blink --docker
bash compile esp32c6 Blink --docker
Platform→boards mapping is defined in ci/docker_utils/build_platforms.py:
DOCKER_PLATFORMS = {
"avr": ["uno", "attiny85", "nano_every", ...],
"esp": ["esp32dev", "esp32s3", "esp32c3", ...],
# ... etc
}
During image build, ci/docker_utils/build.sh:
Result: One Docker image contains toolchains for entire platform family!
For complete details on the CI/CD Docker system:
| Feature | Local Development | CI/CD Published |
|---|---|---|
| Purpose | Development/testing | Production/CI |
| Location | Built locally | Docker Hub |
| Naming | Hash-based (fastled-platformio-uno-abc123) | Platform-level (niteris/fastled-compiler-avr:latest) |
| Container | Board-specific | Platform-level (fastled-compiler-avr) |
| Boards per image | One board | Multiple boards (family) |
| Build trigger | Manual | Daily (2 AM UTC) |
| FastLED source | GitHub release | GitHub release |
| Cache invalidation | Automatic (hash) | Time-based (daily) |
| Best for | Local cross-platform dev | CI/CD, production builds |
ci/build_docker_image_pio.py - Build script for local imagesci/docker_utils/prune_old_images.py - Cleanup script for local imagesci/docker_utils/build_platforms.py - Platform→boards mappingci/docker_utils/DOCKER_DESIGN.md - Complete CI/CD system documentation.github/workflows/build_docker_compiler_*.yml - GitHub Actions workflowsci/docker_utils/Dockerfile.template - Platform-specific Dockerfile (used by both systems)ci/docker_utils/Dockerfile.base - Base image Dockerfile (used by both systems)ci/docker_utils/build.sh - Multi-board build script (used by CI/CD)ci/boards.py - Platform configurations (used by both systems)ci/AGENTS.md - CI/build system documentation