Skip to content

About

Create minimal, hardened container images containing ONLY what you leverage.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

minifier-cli

A tool to create minimal, hardened container images by observing what files your application actually uses at runtime, then building a new image with ONLY those files.

What It Is

minifier-cli creates dramatically smaller and more secure container images by:

  1. Tracing - Monitoring which files your running container actually accesses
  2. Analyzing - Resolving dependencies and following symlinks to find all required files
  3. Repackaging - Building a new "FROM scratch" image with only the essential files

Result: 80-90% size reduction while maintaining full functionality.

How It Works

Technical Implementation

Tracer:

  • Spawns your container normally with Docker
  • Background goroutine polls every 1 second
  • Reads /proc/*/maps for memory-mapped files
  • Reads /proc/*/fd for open file descriptors
  • Writes deduplicated file list to trace log

Analyzer:

  • Parses trace log line-by-line
  • Uses Go's debug/elf package to analyze ELF binaries
  • Extracts imported shared libraries (.so files)
  • Finds dynamic linker (PT_INTERP like /lib64/ld-linux.so)
  • Recursively resolves all dependencies
  • Adds critical system files (passwd, group, hosts, etc.)

Repackager:

  • Extracts Docker metadata via JSON inspect
  • Preserves ENV, CMD, ENTRYPOINT, EXPOSE, VOLUME, LABEL, USER, WORKDIR
  • Creates temp container to copy files from original image
  • Generates optimized Dockerfile
  • Builds new minimal image from scratch

Real-World Use Cases

1. Security Hardening

Problem: Third-party appliances (like Datadog Agent) come with unnecessary tools that increase attack surface.

Solution:

minifier-cli trace start --image datadog/agent:latest --name dd-prod
# Let it run for 24 hours in production-like environment
# Ctrl+C to stop
minifier-cli repackage --name dd-prod --output datadog-minimal:prod

Result: 1.2GB → 150MB (87% reduction), removed shells, package managers, debug tools

2. Regulatory Compliance (NIST, PCI-DSS)

Problem: Need to prove minimal attack surface for compliance audits.

Benefits:

  • Documents exact file manifest for auditors
  • Passes "principle of least privilege" requirements
  • Easier security scanning (fewer files to check)
  • Reproducible minimal builds

3. Legacy Application Migration

Problem: Ancient application with unknown dependencies.

minifier-cli trace start --image legacy-app:old --name legacy
# Run comprehensive test suite to exercise all code paths
# Ctrl+C when done
minifier-cli repackage --name legacy --output legacy-app:minimal

Result: Discovered exact runtime requirements, 2.5GB → 300MB

4. CI/CD Optimization

Problem: Slow Docker pulls, high registry costs.

Benefits:

  • Faster deployments (nginx: 80MB → 14MB)
  • Lower storage costs across thousands of deployments
  • Reduced bandwidth usage
  • Faster autoscaling

5. IoT / Edge Deployments

Problem: Limited disk space on edge devices.

Benefits:

  • Fits on resource-constrained devices (Node.js: 900MB → 120MB)
  • Faster OTA updates
  • More applications per device

6. Multi-Stage Build Optimization

Problem: Unsure which files to include in final stage.

Solution: Trace to discover actual dependencies, then use manifest to inform Dockerfile COPY statements.

Comparison to Alternatives

vs. Multi-stage Docker builds:

  • ✅ Automatic (no manual file selection)
  • ✅ Based on runtime data (not guesswork)
  • ✅ Works with ANY existing image

vs. Distroless images:

  • ✅ Works with existing images (no rebuild needed)
  • ✅ Preserves your exact dependencies
  • ✅ No need to start from scratch

vs. Docker Slim:

  • ✅ Written in Go (easier to extend)
  • ✅ Simple three-step workflow
  • ✅ Preserves all Docker metadata

Installation

Build from Source

git clone https://github.com/swantron/minifier-cli
cd minifier-cli
go build -o minifier-cli .

Best Practices

Tracing Guidelines

Trace Representative Workloads:

  • Run for 24-48 hours in production-like environment
  • Execute comprehensive test suite during trace
  • Exercise all code paths and features
  • Include edge cases and error scenarios

Important Considerations:

⚠️ Startup files must be accessed:

  • Entrypoint scripts need to execute
  • May need multiple trace sessions for different modes

⚠️ Dynamic loading:

  • Plugins loaded at runtime must be exercised
  • Database drivers loaded by name need testing
  • Configuration-dependent features must be triggered

✅ Validation:

  • Thoroughly test minified image before production
  • Keep original image as fallback
  • Monitor for missing files in staging environment

When to Use

Perfect for:

  • Security hardening existing third-party images
  • Compliance requirements (minimal attack surface)
  • Optimizing legacy applications with unknown dependencies
  • Reducing deployment costs and bandwidth
  • Edge/IoT deployments with space constraints
  • Discovering actual runtime dependencies

Not ideal for:

  • Images you fully control and can rebuild properly
  • Development environments (need debug tools)
  • Images with frequently changing behavior
  • Applications with complex dynamic loading

Usage

Interactive Mode (v0.1)

The tool uses a stateful, multi-step workflow:

1. Start Tracing

minifier-cli trace start \
  --image nginx:alpine \
  --name my-nginx

This command:

  • Launches the container with its default configuration
  • Attaches file access monitor to capture accessed files
  • Writes traced files to temp directory
  • Runs in foreground (Ctrl+C to stop)

Note: The tracer process must stay running to capture files. Run it in a terminal you can keep open, or use a terminal multiplexer like tmux.

2. Exercise the Application

While the tracer is running, interact with your container to generate trace data:

# In another terminal, generate traffic
curl http://localhost:8080
curl http://localhost:8080/api/health

# Exercise all features you need
# Send production-like traffic
# Run your test suite

Run for a representative period (minutes to hours) to capture all necessary files.

⚠️ WARNING: Your minified image will ONLY contain files accessed during this trace. Make sure to exercise every feature you intend to use in production.

3. Stop Tracing

Press Ctrl+C in the terminal running the tracer.

The container continues running in the background. The trace log is saved and ready for analysis.

4. Repackage

minifier-cli repackage \
  --name my-nginx \
  --output nginx-minimal:latest

This command:

  • Reads the trace log
  • Analyzes ELF dependencies and resolves symlinks
  • Extracts Docker metadata from the original image
  • Copies only the necessary files
  • Builds a new "FROM scratch" image

Result: A dramatically smaller image with the same functionality.

Example: Complete Workflow

# 1. Start tracing nginx
minifier-cli trace start --image nginx:alpine --name web

# 2. In another terminal, generate traffic for 5 minutes
for i in {1..300}; do
  curl -s http://localhost:80 > /dev/null
  sleep 1
done

# 3. Stop tracing (Ctrl+C in tracer terminal)

# 4. Create minified image
minifier-cli repackage --name web --output nginx-minimal:prod

# 5. Compare sizes
docker images | grep nginx
# nginx:alpine        80.9MB
# nginx-minimal:prod  14MB   (83% reduction!)

# 6. Test the minified image
docker run -d -p 8080:80 nginx-minimal:prod
curl http://localhost:8080  # Should work!

Advanced Options

You can also use a trace log directly:

minifier-cli repackage \
  --log-file /path/to/trace.log \
  --output minified-image:latest

How It Works (Summary)

  1. Userspace Tracing: Polls /proc/*/fd and /proc/*/maps every second to capture all file accesses from within the running container
  2. Dependency Resolution: Resolves symlinks, ELF binary dependencies, and adds essential system files
  3. Image Repackaging: Copies traced files from the original image and builds a new minimal image from scratch

Architecture

minifier-cli/
├── cmd/              # CLI commands (cobra)
│   ├── root.go       # Root command setup
│   ├── trace.go      # trace start/stop commands
│   └── repackage.go  # repackage command
├── pkg/
│   ├── session/      # Session state management
│   ├── tracer/       # eBPF tracer implementation
│   ├── analyzer/     # Dependency analysis engine
│   └── repackager/   # Docker image builder
└── main.go           # Entry point

Requirements

  • Docker installed and running
  • Linux kernel with eBPF support (for tracing)
  • CAP_SYS_ADMIN and CAP_SYS_PTRACE capabilities

Limitations

  • v0.1 does not support Java/JVM applications
  • Requires thorough manual testing to ensure complete coverage
  • Only captures file accesses during the trace period

Future Roadmap

  • v0.2: CI/Test-Suite Mode - Automated tracing with test suites
  • Recipe files for common applications (e.g., datadog.recipe.sh)
  • Enhanced ELF dependency resolution
  • Support for more runtime environments

License

MIT — see LICENSE.

Contributing

Contributions are welcome. See CONTRIBUTING.md for guidelines.

Testing

Quick Test

Run the automated demo:

./demo.sh

Or run the full test suite:

./test.sh

See TESTING.md for detailed testing instructions and scenarios.

Run Unit Tests

go test ./...

About

Create minimal, hardened container images containing ONLY what you leverage.

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages