Few months ago I came with an idea to build Composer’s Docker images, but containing only binary file. Yesterday my pull request was merged, and now it’s possible to use such images in your own Docker builds as the simplest way to get Composer binary in your own image! 😁
💥 I'm happy to announce that my PR to #Composer was merged, so soon it'll be possible to improve #Docker build and copy Composer's binary from low-size (~2.5MB), binary-only images 🥳
— Greg Korba 🛠️🛹 Codito (@_Codito_) October 31, 2022
ℹ️ Short `composer` alias is not supported.https://t.co/wIxFefWaVP
Happy #PHP building! 😁 pic.twitter.com/0SX1tg5e3R
Explanation
If you want to include Composer in your Docker builds, you have several options for that. But previously if you wanted to use COPY --from=composer
approach, you had to download ~190MB image of Composer just to pull out one ~2.5MB file from it. Obviously, it wasn’t optimal, even when we consider Docker build cache.
After my changes, every Composer’s Docker image will have binary-only equivalent. There are 3 differences though:
composer/composer
image must be used (details below)-bin
suffix has to be added- Composer binary is located in the root directory (
/composer
), unlike in the full image (/usr/bin/composer
)
For example, if you want to install the latest release from v2 branch, you need:
FROM php:8-alpine
COPY --from=composer/composer:2-bin /composer /usr/bin/composer
When to use it (or not)
Binary-only images are useful only when you build your own PHP-based images, and you want to install Composer there too. Instead of installing it programmatically, you can use Docker image and extract ready-to-use binary file from it.
Having said that, it seems obvious that binary-only images are not suitable to run anything using them. Running docker run -it --rm composer/composer:2-bin <anything>
won’t work because those images do not contain anything more than Composer’s binary - there is no PHP runtime or shell.
Implementation details
In mentioned Pull Request there are 2 kind of changes:
- build targets were defined in
Dockerfile
s in order to be able to build both type of images - GitHub Actions were modified, so every pipeline contains Docker build both for regular and binary-only images, which are tagged respectively
The first change is more interesting, so let’s look at the changes for 2.4
branch:
diff --git a/2.4/Dockerfile b/2.4/Dockerfile
index c0b4ca7..866be1c 100644
--- a/2.4/Dockerfile
+++ b/2.4/Dockerfile
@@ -1,4 +1,4 @@
-FROM php:8-alpine
+FROM php:8-alpine AS binary-with-runtime
RUN set -eux ; \
apk add --no-cache --virtual .composer-rundeps \
@@ -89,3 +89,10 @@ WORKDIR /app
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["composer"]
+
+FROM scratch AS standalone-binary
+
+COPY --from=binary-with-runtime /usr/bin/composer /composer
+
+# This is defined as last target to be backward compatible with build without explicit --target option
+FROM binary-with-runtime AS default
First of all, binary-with-runtime
name was added to existing build, which is then used in two ways: for building full image (docker build --target binary-with-runtime
), and as a base for binary-only image (COPY --from=binary-with-runtime /usr/bin/composer /composer
). The latter is called multi-stage build, and it’s a way to split build into many independent stages, from which some files can be copied to other layers. It’s helpful especially for optimising final image size, because e.g. any temporary files can be easily omitted.
The last line is interesting though - it ensures backward compatibility by aliasing binary-with-runtime
with default
stage. Since this is last stage in the file, it will be used when --target
is not defined in build command.
Summary
It is small, but really important and helpful change, that can optimise many existing, real-world pipelines. Try it out in your build and let me know what you think 🙂