Documents the multi-stage approach: build Flutter from source in Debian, package as APK with melange, compose minimal runtime image from Wolfi packages with apko. Phased rollout starting with web variant PoC. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
8.9 KiB
8.9 KiB
Custom Minimal Flutter SDK Distribution
Goal
Build Flutter SDK from source in a multi-stage Docker build, then copy only the compiled SDK and its runtime dependencies into a custom minimal Linux base image. The result is a secure, minimal container with zero build toolchain bloat.
Architecture
┌─────────────────────────────────────┐
│ Stage 1: BUILD (debian:bookworm) │
│ │
│ - Full build toolchain │
│ - Compile Flutter engine + Dart │
│ - All build-time deps │
│ - Discarded after build │
└──────────────┬──────────────────────┘
│ COPY artifacts
▼
┌─────────────────────────────────────┐
│ Stage 2: RUNTIME (custom base) │
│ │
│ - Minimal glibc + runtime libs │
│ - Flutter SDK (pre-compiled) │
│ - Only what's needed to run │
│ `flutter build` commands │
└─────────────────────────────────────┘
Approach: apko + melange on Wolfi
Use Chainguard's open-source tooling to build the runtime base image from Wolfi packages. This avoids maintaining a full Linux distribution while achieving the same result.
Why Wolfi (not Alpine, not from scratch)
- glibc-based: Flutter/Dart require glibc (musl/Alpine won't work)
- apk package format: Minimal, fast, composable
- 15,000+ packages: glibc, libstdc++, git, curl, ca-certificates all available
- Nightly CVE patching: Chainguard patches 80%+ of vulnerabilities within 24 hours
- No distro maintenance burden: We consume their packages, not maintain them
Tools
| Tool | Purpose |
|---|---|
| melange | Build custom APK packages (Flutter SDK, Android SDK) from source |
| apko | Compose final OCI image from APK packages (Wolfi base + custom) |
Stage 1: Build Flutter from Source
Flutter SDK source build requirements:
- git (clone flutter/flutter and flutter/engine repos)
- Python 3 (engine build scripts)
- curl, unzip, xz-utils
- clang/llvm (engine compilation)
- ninja-build, gn (engine build system)
- libstdc++-dev
- Dart SDK (bootstrapped from prebuilt, then rebuilt)
Flutter engine build steps:
- Clone
flutter/engineat the pinned revision fromflutter/flutter/bin/internal/engine.version - Run
gclient syncto fetch dependencies - Build with
gn+ninjafor target platform - Dart SDK is built as part of the engine
Per-Variant Build Targets
| Variant | Engine build target | Extra build deps | Extra runtime deps |
|---|---|---|---|
| web | host_release (dart2js/dart2wasm) |
None | None |
| android | android_release + android_release_arm64 |
Android NDK | JDK 17, Android SDK cmdline-tools |
| linux | linux_release_x64 |
GTK3-dev, CMake | GTK3 runtime libs |
Stage 2: Custom Runtime Base via apko
Web variant (minimal)
# apko.web.yaml
contents:
keyring:
- https://packages.wolfi.dev/os/wolfi-signing.rsa.pub
repositories:
- https://packages.wolfi.dev/os
- @local /packages # our melange-built Flutter SDK package
packages:
- wolfi-baselayout
- glibc
- libstdc++
- git
- ca-certificates-bundle
- flutter-sdk-web@local
entrypoint:
command: /bin/sh
environment:
FLUTTER_HOME: /opt/flutter
PATH: /opt/flutter/bin:/opt/flutter/bin/cache/dart-sdk/bin:/usr/bin:/bin
archs:
- x86_64
Android variant
Same as web plus:
openjdk-17-jre(from Wolfi)flutter-sdk-android@local(custom package including Android SDK components)
Linux desktop variant
Same as web plus:
gtk+3.0runtime libs (from Wolfi, or custom melange package if not available)flutter-sdk-linux@local
melange Package Definitions
flutter-sdk-web.melange.yaml (example structure)
package:
name: flutter-sdk-web
version: 3.38.9
epoch: 0
description: Flutter SDK for Web/WASM builds (compiled from source)
environment:
contents:
packages:
- build-base
- git
- python3
- curl
- unzip
- xz
- clang
- ninja
- gn
pipeline:
- uses: git-checkout
with:
repository: https://github.com/flutter/flutter.git
tag: ${{package.version}}
destination: flutter
- runs: |
# Bootstrap Dart SDK
flutter/bin/flutter --version
# Configure for web-only
flutter/bin/flutter config --enable-web \
--no-enable-android --no-enable-ios \
--no-enable-linux-desktop --no-enable-macos-desktop \
--no-enable-windows-desktop
# Precache web artifacts
flutter/bin/flutter precache --web \
--no-android --no-ios --no-linux \
--no-macos --no-windows --no-fuchsia --no-universal
# Install to package destination
mkdir -p ${{targets.destdir}}/opt
mv flutter ${{targets.destdir}}/opt/flutter
# Mark git safe directory
git config --global --add safe.directory /opt/flutter
Implementation Phases
Phase 1: Proof of Concept (web variant only)
- Install melange and apko locally
- Write
flutter-sdk-web.melange.yamlto package Flutter SDK - Build the APK:
melange build flutter-sdk-web.melange.yaml - Write
apko.web.yamlto compose the final image - Build the image:
apko build apko.web.yaml flutter-sdk-web:test flutter-sdk-web.tar - Load and test:
docker load < flutter-sdk-web.tar - Verify:
docker run --rm flutter-sdk-web:test flutter doctor -v - Compare image size vs current debian:bookworm-slim approach
Phase 2: CI Pipeline
- Add melange + apko build steps to publish.yaml
- Multi-stage: melange builds APK → apko composes image → push to registry
- Scout CVE scan on the final image
- SBOM generation (apko generates SBOMs natively)
Phase 3: Android + Linux Desktop Variants
- Write melange packages for Android SDK components
- Write apko YAML for Android variant (add JDK from Wolfi)
- Write apko YAML for Linux variant (add GTK3 from Wolfi)
- Test each variant with real Flutter project builds
Phase 4: Automated Updates
- Daily update-check detects new Flutter stable release
- Triggers melange rebuild of Flutter SDK package
- apko recomposes image with latest Wolfi packages (picks up CVE fixes)
- Push new images to registry
Expected Image Sizes
| Variant | Current (debian:bookworm-slim) | Target (Wolfi/apko) |
|---|---|---|
| Web | ~1.8 GB | ~1.2-1.4 GB |
| Android | ~3.5 GB | ~2.5-3.0 GB |
| Linux | ~2.2 GB | ~1.5-1.8 GB |
Note: Flutter SDK itself is ~1+ GB regardless of base. The savings come from the base OS layer (74 MB debian-slim → ~14 MB Wolfi base) and elimination of build-time packages.
File Structure (Target)
docker-flutter-sdk/
├── melange/
│ ├── flutter-sdk-web.melange.yaml
│ ├── flutter-sdk-android.melange.yaml
│ └── flutter-sdk-linux.melange.yaml
├── apko/
│ ├── web.apko.yaml
│ ├── android.apko.yaml
│ └── linux.apko.yaml
├── Dockerfile # Fallback: current debian approach (web)
├── Dockerfile.android # Fallback: current debian approach (android)
├── Dockerfile.linux # Fallback: current debian approach (linux)
├── .gitea/
│ └── workflows/
│ ├── publish.yaml # Build with melange+apko, push to registry
│ ├── publish-dockerfile.yaml # Fallback Dockerfile builds
│ ├── scout.yaml
│ └── update-check.yaml
└── PLAN.md
Risks and Mitigations
| Risk | Mitigation |
|---|---|
| GTK3 dev libs not in Wolfi | Fall back to Dockerfile.linux with debian-slim for that variant |
| Flutter engine source build is complex | Start with packaging prebuilt SDK, graduate to source build |
| melange/apko learning curve | Well-documented by Chainguard, active community |
| Wolfi package availability changes | Pin package versions, maintain fallback Dockerfiles |
| Build times for source compilation | Cache melange build artifacts, only rebuild on new Flutter release |
Dependencies
- melange CLI:
go install chainguard.dev/melange@latest - apko CLI:
go install chainguard.dev/apko@latest - Docker (for testing)
- Signing key pair (for APK package signing)