Skip to content

dawsonlp/scratch-docker-images

Repository files navigation

Scratch Docker Images

This project builds minimal, statically compiled binary-only Docker images from scratch. The images are built using up-to-date base OS images to support the latest async work such as io_uring, but the final concept is to get down to just the application binary without the supporting OS.

Project Structure

scratch_docker_images/
├── build-base/              # Base build environment with latest tools
│   └── Dockerfile          # Fedora-based build environment
├── python-static/          # Static Python build
│   └── Dockerfile          # Multi-stage Python build
├── workspace/              # Development workspace (created on demand)
├── build.sh               # Main build script
├── docker-compose.yml     # Development and testing setup
├── Makefile              # Convenient build targets
└── README.md             # This file

Features

  • Latest Fedora Base: Uses the latest Fedora image with current GCC, glibc, and compilation tools
  • Static Compilation: Produces fully static binaries with no external dependencies
  • Multi-Architecture: Supports both ARM64 and AMD64 architectures
  • Optimized Builds: Uses LTO, PGO, and aggressive optimization flags
  • Minimal Size: Strips binaries and uses scratch base for final images
  • io_uring Support: Includes latest kernel headers for modern async I/O

Current Focus: Python

The first implementation focuses on building a statically compiled Python interpreter:

  • Python 3.13.5 (configurable)
  • Fully static compilation against standard libraries
  • Optimized for size and performance
  • Supports modern Python features and async operations

Quick Start

Prerequisites

  • Docker with buildx support
  • Make (optional, for convenience targets)

Build Everything

# Using the build script (cloud build by default)
./build.sh

# Using local build script (optimized for laptop)
./build-local.sh

# Using Make
make build-cloud    # Multi-architecture cloud build
make build-local    # Single-architecture local build

# Using docker compose
docker compose build

Test the Python Image

# Test Python version
docker run --rm python-static:latest --version

# Test Python functionality
docker run --rm python-static:latest -c "print('Hello from static Python!')"

# Run benchmarks
make benchmark

Build Modes

The project supports two build modes optimized for different use cases:

Cloud Build Mode (Default)

  • Uses Docker cloud buildx (cloud-dawsonlp-arm64)
  • Multi-architecture builds (ARM64 + AMD64)
  • Faster builds with cloud resources
  • Ideal for production and CI/CD
# Cloud build (default)
./build.sh
./build.sh --cloud

# Cloud build with push
./build.sh --push dawsonlp/

# Using Make
make build-cloud
make push-cloud

Local Build Mode

  • Uses local buildx builder
  • Single architecture (AMD64) for speed
  • Optimized for laptop development
  • Faster iteration during development
# Local build
./build.sh --local
./build-local.sh

# Local build with push
./build.sh --local --push dawsonlp/

# Using Make
make build-local
make push-local

Build Options

Using the Build Script

# Build specific Python version
./build.sh --python-version 3.12.7

# Build and push to registry
./build.sh --push dawsonlp/

# Build for specific platform
./build.sh --platform linux/amd64

# Use local builder
./build.sh --local

# Use cloud builder (default)
./build.sh --cloud

# Show help
./build.sh --help

Using Make

# Show all available targets
make help

# Build with specific Python version
make build PYTHON_VERSION=3.12.7

# Quick build (single platform)
make quick-build

# Push to registry
make push-all REGISTRY=dawsonlp/

# Analyze image sizes
make size-analysis

# Run development environment
make dev

Build Process

1. Base Build Environment (build-base)

The base image includes:

  • Latest Fedora with current packages
  • Complete GCC toolchain with static libraries
  • Development tools (cmake, ninja, autotools)
  • SSL/TLS libraries (OpenSSL static)
  • Compression libraries (zlib, bzip2, xz static)
  • Database libraries (SQLite static)
  • Latest kernel headers for io_uring support

2. Static Python Build (python-static)

The Python build process:

  1. Downloads Python source from python.org
  2. Configures with static linking and optimizations
  3. Compiles with LTO and profile-guided optimization
  4. Strips the binary to minimal size
  5. Creates a scratch-based runtime image with only the Python binary

Build Optimizations

  • Link-Time Optimization (LTO): Enables cross-module optimizations
  • Profile-Guided Optimization (PGO): Uses runtime profiles for optimization
  • Static Linking: No external dependencies required
  • Section Garbage Collection: Removes unused code sections
  • Symbol Stripping: Removes debugging symbols for minimal size

Development

Development Environment

# Start development container
make dev

# Or using docker compose
docker compose up -d build-base
docker compose exec build-base /bin/bash

Adding New Languages/Runtimes

To add support for a new language:

  1. Create a new directory (e.g., go-static/, rust-static/)
  2. Create a Dockerfile that uses FROM build-base:latest
  3. Configure static compilation for your language
  4. Update build.sh, Makefile, and docker-compose.yml

Testing

# Test all images
make test

# Test with docker compose
make test-compose

# Run benchmarks
make benchmark

# Analyze sizes
make size-analysis

Configuration

Environment Variables

  • PYTHON_VERSION: Python version to build (default: 3.13.5)
  • REGISTRY: Docker registry prefix (default: empty)
  • PLATFORM: Target platforms (default: linux/amd64,linux/arm64)

Build Arguments

All Dockerfiles support build arguments for customization:

docker build --build-arg PYTHON_VERSION=3.12.7 python-static/

Image Sizes

Expected image sizes (approximate):

  • build-base: ~2GB (full development environment)
  • python-static: ~15-25MB (static Python binary only)

The final runtime images are extremely small because they contain only the statically compiled binary with no OS or libraries.

Multi-Architecture Support

All images support both ARM64 and AMD64 architectures:

# Build for both architectures
docker buildx build --platform linux/amd64,linux/arm64 --tag python-static:latest python-static/

# Build for specific architecture
docker buildx build --platform linux/arm64 --tag python-static:arm64 python-static/

Registry Integration

Pushing to Docker Hub

# Using build script
./build.sh --push dawsonlp/

# Using Make
make push-all REGISTRY=dawsonlp/

Using with CI/CD

The build process is designed to work with CI/CD systems:

# Example GitHub Actions step
- name: Build and push images
  run: |
    ./build.sh --push ${{ secrets.DOCKER_REGISTRY }}/

Performance Considerations

Static Linking Trade-offs

Advantages:

  • No external dependencies
  • Extremely small final images
  • Better security (no shared library vulnerabilities)
  • Consistent behavior across environments

Disadvantages:

  • Larger individual binaries
  • No shared library memory benefits
  • Longer build times
  • More complex debugging

Optimization Flags

The build uses aggressive optimization:

CFLAGS="-O3 -march=native -mtune=native -flto -ffunction-sections -fdata-sections"
LDFLAGS="-Wl,--gc-sections -Wl,--strip-all -static"

Troubleshooting

Common Issues

  1. Build fails with missing dependencies

    • Ensure all static libraries are installed in build-base
    • Check that PKG_CONFIG_PATH includes static library paths
  2. Binary won't run in scratch container

    • Verify static linking: ldd /path/to/binary should show "not a dynamic executable"
    • Check for missing static libraries during build
  3. Large binary sizes

    • Ensure stripping is enabled
    • Consider disabling debug symbols
    • Review linked libraries for unnecessary dependencies

Debug Mode

Build with debug information:

# Modify CFLAGS to include debug info
export CFLAGS="-O3 -g -march=native -mtune=native -flto"
./build.sh

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add your language/runtime support
  4. Test thoroughly on both architectures
  5. Update documentation
  6. Submit a pull request

License

This project is open source. See individual language implementations for their respective licenses.

Roadmap

  • Go static compilation
  • Rust static compilation
  • C/C++ minimal runtime
  • Node.js static build
  • Java native image (GraalVM)
  • .NET native AOT
  • Automated size optimization
  • Security scanning integration
  • Performance benchmarking suite

About

Minimal statically compiled Docker images from scratch - Python, Go, Rust and more

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages