# 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: 1. Clone `flutter/engine` at the pinned revision from `flutter/flutter/bin/internal/engine.version` 2. Run `gclient sync` to fetch dependencies 3. Build with `gn` + `ninja` for target platform 4. 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) ```yaml # 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.0` runtime libs (from Wolfi, or custom melange package if not available) - `flutter-sdk-linux@local` ## melange Package Definitions ### flutter-sdk-web.melange.yaml (example structure) ```yaml 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) 1. Install melange and apko locally 2. Write `flutter-sdk-web.melange.yaml` to package Flutter SDK 3. Build the APK: `melange build flutter-sdk-web.melange.yaml` 4. Write `apko.web.yaml` to compose the final image 5. Build the image: `apko build apko.web.yaml flutter-sdk-web:test flutter-sdk-web.tar` 6. Load and test: `docker load < flutter-sdk-web.tar` 7. Verify: `docker run --rm flutter-sdk-web:test flutter doctor -v` 8. Compare image size vs current debian:bookworm-slim approach ### Phase 2: CI Pipeline 1. Add melange + apko build steps to publish.yaml 2. Multi-stage: melange builds APK → apko composes image → push to registry 3. Scout CVE scan on the final image 4. SBOM generation (apko generates SBOMs natively) ### Phase 3: Android + Linux Desktop Variants 1. Write melange packages for Android SDK components 2. Write apko YAML for Android variant (add JDK from Wolfi) 3. Write apko YAML for Linux variant (add GTK3 from Wolfi) 4. Test each variant with real Flutter project builds ### Phase 4: Automated Updates 1. Daily update-check detects new Flutter stable release 2. Triggers melange rebuild of Flutter SDK package 3. apko recomposes image with latest Wolfi packages (picks up CVE fixes) 4. 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) ## References - [Wolfi Overview](https://edu.chainguard.dev/open-source/wolfi/overview/) - [Getting Started with apko](https://edu.chainguard.dev/open-source/build-tools/apko/getting-started-with-apko/) - [Getting Started with melange](https://edu.chainguard.dev/open-source/build-tools/melange/getting-started-with-melange/) - [Flutter Engine Build Instructions](https://github.com/flutter/flutter/wiki/Compiling-the-engine) - [wolfi-dev/os packages](https://github.com/wolfi-dev/os)