Compare commits

...

3 Commits

Author SHA1 Message Date
mathias 4827388738 Migrate to Wolfi base image and optimize SDK size
Build and Push Flutter SDK Image / build-and-push (Flutter SDK for Linux desktop CI builds, Dockerfile.linux, linux) (release) Successful in 1h8m47s
Build and Push Flutter SDK Image / build-and-push (Flutter SDK for Android CI builds, Dockerfile.android, android) (release) Successful in 1h11m0s
Build and Push Flutter SDK Image / build-and-push (Minimal Flutter SDK for Web/WASM CI builds, Dockerfile, web) (release) Successful in 6m49s
- Switch all Dockerfiles from debian:bookworm-slim to svrnty/base-distro:flutter-sdk-latest (Wolfi)
- Use non-root user (UID 65532) instead of custom flutter user
- Strip wrong-platform engine artifacts per variant (web, android, linux)
- Remove dev/, examples/ and compact .git with git gc --prune=all
- Android: multi-stage build for JDK 17 + Android SDK 36 from Debian
- Linux: multi-stage build for clang/cmake/ninja/GTK3 with glibc conflict resolution
- Update Android SDK from 35 to 36 (required by Flutter 3.38.9)

Image sizes: web 1.32 GB, linux 2.43 GB, android 4.22 GB
(down from 4.9 GB, 3.69 GB, 4.15 GB respectively)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 17:55:20 -05:00
Mathias Beaulieu-Duncan 35cc1cab34 Add non-root flutter user to all SDK images
Check for Flutter SDK Updates / check-update (push) Successful in 2s
Creates a dedicated flutter user and switches to it as the default.
Resolves Docker Scout compliance check for non-root default user.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 01:42:45 -05:00
Mathias Beaulieu-Duncan 301f8e7397 Add plan for custom minimal Flutter SDK distribution using apko/melange
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>
2026-02-02 01:14:31 -05:00
4 changed files with 379 additions and 47 deletions
+15 -17
View File
@@ -1,31 +1,29 @@
FROM debian:bookworm-slim
FROM svrnty/base-distro:flutter-sdk-latest
ARG FLUTTER_VERSION=3.38.9
LABEL org.opencontainers.image.title="flutter-sdk"
LABEL org.opencontainers.image.title="flutter-sdk-web"
LABEL org.opencontainers.image.description="Minimal Flutter SDK for Web/WASM CI builds"
LABEL org.opencontainers.image.version="${FLUTTER_VERSION}"
# Install minimal dependencies for Flutter web builds
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
unzip \
xz-utils \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
USER 0
ENV FLUTTER_HOME=/opt/flutter
ENV PATH="${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin:${PATH}"
# Download Flutter SDK from official archive
# Download Flutter SDK and strip unnecessary files in a single layer
RUN curl -fsSL "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" \
-o /tmp/flutter.tar.xz && \
tar xf /tmp/flutter.tar.xz -C /opt && \
rm /tmp/flutter.tar.xz
rm /tmp/flutter.tar.xz && \
rm -rf /opt/flutter/dev \
/opt/flutter/examples \
/opt/flutter/bin/cache/artifacts/engine/android-* \
/opt/flutter/bin/cache/artifacts/engine/linux-* && \
chown -R 65532:65532 /opt/flutter
# Mark git directory as safe (tarball is owned by different uid)
RUN git config --global --add safe.directory "${FLUTTER_HOME}"
# Mark git directory as safe and compact git history
RUN git config --global --add safe.directory /opt/flutter && \
cd /opt/flutter && git gc --prune=all
USER 65532
# Configure for web-only (disable everything else)
RUN flutter config --enable-web \
+43 -17
View File
@@ -1,27 +1,30 @@
FROM debian:bookworm-slim
FROM svrnty/base-distro:flutter-sdk-latest AS base
ARG FLUTTER_VERSION=3.38.9
ARG ANDROID_SDK_TOOLS_VERSION=11076708
ARG ANDROID_COMPILE_SDK=35
ARG ANDROID_BUILD_TOOLS=35.0.1
ARG ANDROID_COMPILE_SDK=36
ARG ANDROID_BUILD_TOOLS=36.0.0
LABEL org.opencontainers.image.title="flutter-sdk"
LABEL org.opencontainers.image.title="flutter-sdk-android"
LABEL org.opencontainers.image.description="Flutter SDK for Android CI builds"
LABEL org.opencontainers.image.version="${FLUTTER_VERSION}"
# Install dependencies for Flutter + Android SDK
# --- Install JDK and Android SDK in debian (requires apt + shared libs) ---
FROM debian:bookworm-slim AS android-stage
ARG ANDROID_SDK_TOOLS_VERSION=11076708
ARG ANDROID_COMPILE_SDK=36
ARG ANDROID_BUILD_TOOLS=36.0.0
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
unzip \
xz-utils \
ca-certificates \
openjdk-17-jdk-headless \
&& rm -rf /var/lib/apt/lists/*
# Android SDK
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ENV ANDROID_HOME=/opt/android-sdk
ENV PATH="${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${PATH}"
ENV PATH="${JAVA_HOME}/bin:${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${PATH}"
RUN mkdir -p "${ANDROID_HOME}/cmdline-tools" && \
curl -fsSL "https://dl.google.com/android/repository/commandlinetools-linux-${ANDROID_SDK_TOOLS_VERSION}_latest.zip" \
@@ -30,25 +33,48 @@ RUN mkdir -p "${ANDROID_HOME}/cmdline-tools" && \
mv /tmp/cmdline-tools/cmdline-tools "${ANDROID_HOME}/cmdline-tools/latest" && \
rm -rf /tmp/cmdline-tools.zip /tmp/cmdline-tools
# Accept licenses and install SDK components
RUN yes | sdkmanager --licenses > /dev/null 2>&1 && \
sdkmanager --install \
"platform-tools" \
"platforms;android-${ANDROID_COMPILE_SDK}" \
"build-tools;${ANDROID_BUILD_TOOLS}"
# Flutter SDK
ENV FLUTTER_HOME=/opt/flutter
ENV PATH="${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin:${PATH}"
# --- Build final image ---
FROM base
USER 0
# Copy JDK and its required shared libraries from debian
COPY --from=android-stage /usr/lib/jvm/java-17-openjdk-amd64 /usr/lib/jvm/java-17-openjdk-amd64
COPY --from=android-stage /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu
COPY --from=android-stage /lib/x86_64-linux-gnu /lib/x86_64-linux-gnu
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ENV PATH="${JAVA_HOME}/bin:${PATH}"
# Copy Android SDK (already set up with licenses and components)
COPY --from=android-stage /opt/android-sdk /opt/android-sdk
ENV ANDROID_HOME=/opt/android-sdk
ENV PATH="${ANDROID_HOME}/cmdline-tools/latest/bin:${ANDROID_HOME}/platform-tools:${PATH}"
# Download Flutter SDK and strip unnecessary files in a single layer
RUN curl -fsSL "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" \
-o /tmp/flutter.tar.xz && \
tar xf /tmp/flutter.tar.xz -C /opt && \
rm /tmp/flutter.tar.xz
rm /tmp/flutter.tar.xz && \
rm -rf /opt/flutter/dev \
/opt/flutter/examples \
/opt/flutter/bin/cache/artifacts/engine/linux-* \
/opt/flutter/bin/cache/flutter_web_sdk
RUN git config --global --add safe.directory "${FLUTTER_HOME}"
RUN git config --global --add safe.directory /opt/flutter && \
cd /opt/flutter && git gc --prune=all
# Configure for Android-only
# Fix ownership before switching to flutter user
RUN chown -R 65532:65532 /opt/flutter "${ANDROID_HOME}"
USER 65532
# Configure Flutter for Android-only
RUN flutter config --enable-android \
--no-enable-web \
--no-enable-ios \
+62 -13
View File
@@ -1,18 +1,15 @@
FROM debian:bookworm-slim
FROM svrnty/base-distro:flutter-sdk-latest AS base
ARG FLUTTER_VERSION=3.38.9
LABEL org.opencontainers.image.title="flutter-sdk"
LABEL org.opencontainers.image.title="flutter-sdk-linux"
LABEL org.opencontainers.image.description="Flutter SDK for Linux desktop CI builds"
LABEL org.opencontainers.image.version="${FLUTTER_VERSION}"
# Install dependencies for Flutter + Linux desktop builds
# --- Install Linux desktop build deps in debian ---
FROM debian:bookworm-slim AS deps-stage
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
git \
unzip \
xz-utils \
ca-certificates \
clang \
cmake \
ninja-build \
@@ -22,17 +19,69 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
libstdc++-12-dev \
&& rm -rf /var/lib/apt/lists/*
ENV FLUTTER_HOME=/opt/flutter
ENV PATH="${FLUTTER_HOME}/bin:${FLUTTER_HOME}/bin/cache/dart-sdk/bin:${PATH}"
# --- Build final image ---
FROM base
USER 0
# Download Flutter SDK and strip unnecessary files in a single layer
RUN curl -fsSL "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz" \
-o /tmp/flutter.tar.xz && \
tar xf /tmp/flutter.tar.xz -C /opt && \
rm /tmp/flutter.tar.xz
rm /tmp/flutter.tar.xz && \
rm -rf /opt/flutter/dev \
/opt/flutter/examples \
/opt/flutter/bin/cache/artifacts/engine/android-* \
/opt/flutter/bin/cache/flutter_web_sdk
RUN git config --global --add safe.directory "${FLUTTER_HOME}"
RUN git config --global --add safe.directory /opt/flutter && \
cd /opt/flutter && git gc --prune=all
# Configure for Linux desktop only
# Remove symlinks that conflict with COPY from debian stage
RUN rm -f /usr/lib/terminfo
# Copy only specific toolchain binaries (not all of /usr/bin which would overwrite Wolfi's core utils)
COPY --from=deps-stage /usr/bin/cmake /usr/bin/cmake
COPY --from=deps-stage /usr/bin/ninja /usr/bin/ninja
COPY --from=deps-stage /usr/bin/pkg-config /usr/bin/pkg-config
COPY --from=deps-stage /usr/bin/clang-14 /usr/bin/clang-14
RUN ln -sf clang-14 /usr/bin/clang && ln -sf clang-14 /usr/bin/clang++
# Copy libraries from debian stage (specific subdirectories to avoid overwriting Wolfi's core libs)
COPY --from=deps-stage /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu
COPY --from=deps-stage /usr/lib/llvm-14 /usr/lib/llvm-14
COPY --from=deps-stage /usr/lib/cmake /usr/lib/cmake
COPY --from=deps-stage /usr/lib/gcc /usr/lib/gcc
COPY --from=deps-stage /usr/include /usr/include
COPY --from=deps-stage /usr/share/cmake-3.25 /usr/share/cmake-3.25
COPY --from=deps-stage /usr/share/pkgconfig /usr/share/pkgconfig
COPY --from=deps-stage /lib/x86_64-linux-gnu /lib/x86_64-linux-gnu
# Remove Debian's glibc/ld files that conflict with Wolfi's newer glibc,
# then register the Debian library paths and rebuild the linker cache
RUN rm -f /usr/lib/x86_64-linux-gnu/libc.so* /usr/lib/x86_64-linux-gnu/libm.so* \
/usr/lib/x86_64-linux-gnu/libpthread* /usr/lib/x86_64-linux-gnu/libdl.so* \
/usr/lib/x86_64-linux-gnu/librt.so* /usr/lib/x86_64-linux-gnu/libresolv* \
/usr/lib/x86_64-linux-gnu/libmvec* /usr/lib/x86_64-linux-gnu/libnss_* \
/usr/lib/x86_64-linux-gnu/ld-linux* /usr/lib/x86_64-linux-gnu/crt*.o \
/usr/lib/x86_64-linux-gnu/libpcre2-8.so* \
/lib/x86_64-linux-gnu/libc.so* /lib/x86_64-linux-gnu/libc-* \
/lib/x86_64-linux-gnu/libm.so* /lib/x86_64-linux-gnu/libm-* \
/lib/x86_64-linux-gnu/libpthread* /lib/x86_64-linux-gnu/libdl.so* \
/lib/x86_64-linux-gnu/librt.so* /lib/x86_64-linux-gnu/libresolv* \
/lib/x86_64-linux-gnu/libmvec* /lib/x86_64-linux-gnu/libnss_* \
/lib/x86_64-linux-gnu/ld-linux* \
/lib/x86_64-linux-gnu/libpcre2-8.so* && \
echo "/usr/lib/x86_64-linux-gnu" > /etc/ld.so.conf.d/debian-x86_64.conf && \
echo "/lib/x86_64-linux-gnu" >> /etc/ld.so.conf.d/debian-x86_64.conf && \
ldconfig
# Fix ownership before switching to flutter user
RUN chown -R 65532:65532 /opt/flutter
USER 65532
# Configure Flutter for Linux desktop only
RUN flutter config --enable-linux-desktop \
--no-enable-web \
--no-enable-android \
+259
View File
@@ -0,0 +1,259 @@
# 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)