Skip to content

perf(docker): move the runtime images to ubi9-micro and drop gosu - #33

Merged
hectorvent merged 1 commit into
mainfrom
perf/ubi9-micro-image
Sep 7, 2026
Merged

perf(docker): move the runtime images to ubi9-micro and drop gosu#33
hectorvent merged 1 commit into
mainfrom
perf/ubi9-micro-image

Conversation

@hectorvent

Copy link
Copy Markdown
Contributor

Summary

Completes ecosystem decision 0018 for this repo, porting floci-aws's image-size chain: #3037, #3085, #3108.

The pilot already proved it. floci-gcp #185 merged 2026-09-07 and its nightly went 87.9 MB → 53.4 MB compressed, a 39% cut, with all eight compat legs green. That was the gate 0018 set for this repo.

The three upstream commits are one change, not a menu: ubi9-micro ships no useradd and no curl, so the gosu removal and the bash health probe are what make the smaller base viable.

File Change
docker/entrypoint.sh privilege drop moves from gosu to coreutils chroot --userspec=1001:0 --groups=... --skip-chdir /
docker/Dockerfile.native drops the ubi9:9.7 tools stage; runtime is ubi9-micro:9.7, user is one /etc/passwd line
docker/Dockerfile.native-package same base and user setup, replacing ubi9-minimal plus an inline gosu install. This is the image release.yml publishes
docker/Dockerfile drops its gosu download
docker/Dockerfile.compat ubi9-micro has no package manager, so this can no longer extend the native image: ubi9-minimal tools stage, copies /app out, plus the portable Python trims from #3108
docker/healthcheck.sh new. The old probe used curl, which ubi9-micro does not have
pom.xml, application.yml exclude the unused brotli4j platform JNI jars; drop two native-image args that are no-ops since GraalVM 22.1

One extra trim, specific to this repo

The compat image ran python3.11 -m pip install --upgrade pip, which leaves a second, shadowing copy of pip in /usr/local while the RPM's stays in /usr/lib. floci-aws never did this. Measured on the equivalent floci-gcp image: 3.7 MB uncompressed, with pip and the SDKs still working from the RPM copy. Small, but free, and it converges on floci-aws's shape.

The divergence from floci-aws, worth a reviewer's eye

floci-aws's JVM image is Debian noble. This repo's is eclipse-temurin:25-jre-alpine, where busybox chroot has no --userspec:

chroot: can't change root directory to '--userspec=1001:0': No such file or directory

It works only because that image ships GNU coreutils at /usr/sbin/chroot, ahead of busybox on PATH. Verified by running it, not inferred.

Verified on a built runtime image

  • uid=1001(floci) gid=0(root) through the entrypoint
  • a socket owned by gid 999 yields groups=0(root),999, so the by-number supplementary group works with no /etc/group entry
  • /app/data writable
  • healthcheck.sh exits 0 on a 200 and 1 on a 500

The ~/.oci/config symlinks now run in the final stage, after the /app copy, so the copy cannot shadow /app/.oci. The compat image keeps no USER directive, as before, and that comment is updated since it named gosu.

The real numbers land when CI publishes. This image starts at 72.5 MB rather than gcp's 87.9, so the absolute saving will differ; the compat image will move much less than the native one, as it did in gcp (6% vs 39%), because its OCI CLI and Python layers dominate.

Type of change

  • Bug fix (fix:)
  • New feature (feat:)
  • Breaking change (feat!: or fix!:)
  • Docs / chore

OCI Compatibility

No wire protocol, endpoint or emulated behavior changes. Packaging only. The compat suite against a real native build is what this PR's CI is for, and it is the check that matters: the entrypoint change alters how Functions and OKE reach the Docker socket.

Checklist

  • Tests pass locally (no source changed; runtime image behavior verified as above)
  • New or updated integration test added (not applicable, packaging change)
  • Commit messages follow Conventional Commits

Ports floci-aws #3037, #3085 and #3108, completing ecosystem decision 0018.
The floci-gcp pilot merged 2026-09-07 and its nightly went 87.9 MB to 53.4 MB
compressed, a 39% cut, with all eight compat legs green; that was the gate for
this repo.

The chain, and it is a chain: ubi9-micro ships no useradd and no curl, so the
gosu removal and the bash health probe are what make the smaller base viable.

- entrypoint.sh: the privilege drop moves from gosu to coreutils
  `chroot --userspec=1001:0 --groups=... --skip-chdir /`. Supplementary groups
  pass by number, so the Docker socket's gid needs no /etc/group entry, and
  --skip-chdir keeps /app as the working directory where ./data resolves.
- Dockerfile.native: the ubi9:9.7 tools stage that fetched gosu and installed
  shadow-utils is gone; runtime is ubi9-micro:9.7 and the user is one
  /etc/passwd line.
- Dockerfile.native-package: same base and user setup, replacing ubi9-minimal
  plus an inline gosu install. This is the image release.yml publishes, and it
  also drops a curl-based healthcheck the new base could not run.
- Dockerfile: drops its gosu download. Unlike floci-aws's Debian base, this one
  is Alpine, where busybox chroot has no --userspec; it works because the image
  ships GNU coreutils at /usr/sbin/chroot, ahead of busybox on PATH. Verified.
Also drops `pip install --upgrade pip` from the compat image. It left a second,
shadowing copy of pip in /usr/local while the RPM's stayed in /usr/lib; measured
at 3.7 MB uncompressed on the equivalent floci-gcp image, with pip and the SDKs
still working from the RPM copy. floci-aws never did this upgrade.
- Dockerfile.compat: ubi9-micro has no package manager, so this can no longer
  extend the native image. It gets a ubi9-minimal tools stage and copies /app
  out, plus the portable Python trims from #3108.
- healthcheck.sh: new, shared by the Dockerfiles. The old probe used curl, which
  ubi9-micro does not have, or wrote to /dev/tcp without reading the response;
  this one requires 200.
- pom.xml: exclude the brotli4j platform JNI jars, which are never used.

Verified on a built runtime image: uid=1001(floci) gid=0(root) through the
entrypoint; a socket owned by gid 999 yields groups=0(root),999 end to end;
/app/data writable; healthcheck exits 0 on a 200 and 1 on a 500.

The USER directive stays absent on the compat image, as before: the image runs
as root so entrypoint.sh can fix /app/data ownership before dropping privileges.
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown

🎉 Thanks for your first pull request to Floci!

Your CI checks need a maintainer to approve them before they run. That is GitHub's standard gate on first-time contributors, not a problem with your PR — so if the checks look like they are doing nothing, that is why. Once a maintainer approves, CI and the compatibility suite start automatically. Nothing is needed from you in the meantime.

While you wait, a couple of things that make review faster:

  • Link the issue this fixes with Closes #N in the description
  • Commits follow Conventional Commits (feat(s3): ..., fix(dynamodb): ...)
  • Behaviour changes come with a test — see CONTRIBUTING.md

Come join us in Slack — it is the fastest way to reach maintainers if you get stuck, or want feedback on an approach before investing more time in it.

@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown

Greptile Summary

This PR reduces runtime image size by moving the native images to UBI 9 Micro, replacing gosu with coreutils chroot, introducing a Bash TCP health probe, and rebuilding the compatibility image from a UBI minimal tools stage.

  • Removes gosu and package-manager dependencies from native runtime images.
  • Preserves Docker-socket access through a numeric supplementary group during privilege drop.
  • Trims compatibility-image Python files and unused Brotli platform artifacts.
  • Contains a compat-image build-order defect: useradd runs before shadow-utils is installed.

Confidence Score: 4/5

The PR is not safe to merge until the compatibility tools stage creates its runtime user without invoking a command absent from the base image.

Release and nightly compatibility-image builds stop at useradd because the fresh UBI minimal stage has not installed shadow-utils; the remaining reviewed runtime and packaging changes have no established failure.

Files Needing Attention: docker/Dockerfile.compat

Important Files Changed

Filename Overview
docker/Dockerfile.compat Rebuilds the compatibility image on UBI minimal, but calls the unavailable useradd command before installing shadow-utils.
docker/entrypoint.sh Replaces gosu with a numeric UID/GID privilege drop using GNU coreutils chroot.
docker/healthcheck.sh Adds a curl-free TCP health probe that requires an HTTP 200 response.
docker/Dockerfile.native-package Moves the published native runtime to UBI 9 Micro and creates its user through /etc/passwd.
docker/Dockerfile.native Aligns locally built native images with the smaller UBI 9 Micro runtime.
docker/Dockerfile Removes gosu from the Alpine JVM image and relies on its GNU coreutils chroot.
pom.xml Excludes unused platform-specific Brotli JNI artifacts.
src/main/resources/application.yml Removes obsolete native-image arguments while retaining required protocol and initialization settings.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Native build artifacts] --> B[Dockerfile.native-package]
  B --> C[UBI 9 Micro native image]
  C -->|copy /app and entrypoint| D[Dockerfile.compat]
  E[UBI 9 Minimal tools stage] -->|Python SDK and OCI CLI| D
  D --> F[Compatibility image]
  G[entrypoint.sh] -->|coreutils chroot| C
  G -->|coreutils chroot| F
  H[healthcheck.sh] --> C
  H --> F
Loading

Reviews (1): Last reviewed commit: "perf(docker): move the runtime images to..." | Re-trigger Greptile

Comment thread docker/Dockerfile.compat
@hectorvent
hectorvent merged commit 018c207 into main Sep 7, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant