Small tip to reduce your Docker images size
Tags: docker, ruby, python, tutorial
Published: 2022-05-05 12:59:12

Hi everyone, first post on dev.to, I whish to be more active in the future!

Today I want to explain a small tip I recently fond out to reduce the Docker images size, in my case I was able to reduce the size by half!

Let's assume that you are working with a language like Ruby or Python, even if these languages are not compiled they often need some system libraries to work properly, in particular if you are working with databases (MySQL, SQLite, Postgres) you need to compile the gem or the library for the specific architecture of your machine.

In my case I'm working with Ruby and I'm trying to install the SQLite gem that, unfortunately, requires the `build-base` system library.

Here is the project I'm working on:
[Faenz Analytics](https://github.com/a-chris/faenz)

So I thought to install it, compile the SQLite gem and uninstall everything I do not need anymore, here is my Dockerfile:

```
FROM ruby:3.0.4-alpine3.15
WORKDIR /faenz-analytics
RUN apk update && \
    apk add make && \
    apk add build-base && \ # this takes 200Mb of space!!
    apk add sqlite-dev

# ...some instructions

RUN bundle install  # installing Ruby gems
RUN apk del build-base

# ...some other instructions
```

easy, right? we installed `build-base`, compiled and installed the gems and removed `build-base` to reduce the image size. 

**The problem is that this does not work! The image size is the same as if we did not remove `build-base`. Let's discover why.**

We wrote three `RUN` instructions, each one generates a layer and these layer are put togheter by Docker to create the final image:

1. the first `RUN` installs the system libraries
2. the second `RUN` install the gems/libraries by Ruby or whatever language you are using
3. the third `RUN` removes the system libraries

since we installed the system libraries in one of the layer they will take some space and will increase the image size, there's no way we can reduce the image size with the next layers.

It's like a sum with no negative numbers. The size can only increase or stay the same from a layer to another.

## What? Don't give up, we got this 🚀

**Here is the solution: put these operations in the same layer!**

```
FROM ruby:3.0.4-alpine3.15
WORKDIR /faenz-analytics
RUN apk update && \
    apk add make && \
    apk add sqlite-dev

# ...some instructions

RUN apk add build-base && \
    bundle install && \
    apk del build-base

# ...some other instructions
```

grouping the operations in just one `RUN` instruction avoids Docker to waste space to store the `build-base` in a layer. The layer is generated at the end of the instruction so we are going to install, use the `build-base` and prontly remove it so that the layer will not contain it at all.

With this little tip my image size went from 400Mb to 170mb, less than half!

If you want to exagerate with the micro optimizations, you can apply the same solution to `apk update` (or `apt-get update` for debian/ubuntu images) by deleting the cache created by this command:

```
RUN apk update && \
    add build-base && \
    # install other dependencies here...
    bundle install && \
    apk del build-base && \
    rm -rf /var/cache/apk && \
    rm -rf tmp/cache
```

I'm not a Docker expert, so, if you know a better solution or want to share similar tips to reduce the image size please leave a comment, I'm all ears 🙏