Alpine Based Docker Images Make a Difference in Real World Apps


Reading Time: 4 minutes

This article was originally published by Nick Janetakis on his personal site, and with his permission, we are sharing it here for Codeship readers.

I develop a lot of Python and Ruby based applications, and my Dockerfile typically pulls in from the official Python or Ruby images on the Docker Hub.

Up until recently, you only had 3 choices as a base OS. You could choose to use Jessie, Wheezy or Slim. However, now you can also choose Alpine as a base.

I imagine most people used the Slim variant, and then installed things like build-essential in their Dockerfile if they needed it to compile libraries that their app’s packages use.

In this post you’re going to see how Alpine compares to Debian Jessie (Slim) and also learn how to optimize the Alpine version even more.

What’s considered a real world application?

I’m currently in the middle of creating a new demo application for the Build a SAAS App With Flask course that I’m remaking.

It currently has around 30 top level Python packages and thousands of lines of code. It is a real application that connects to multiple databases and a few package dependencies require libraries to be compiled to function.

I’m a huge backer of Docker, not in a financial sense but I truly believe it’s a great tool so naturally I’m going to be using Docker in the course.

How does Alpine compare to Debian Jessie (Slim)?

python:2.7-slim as a base for my Dockerfile produces a 467.4MB image. python:2.7-alpine as a base for my Dockerfile produces a 309.1MB image.

That’s a huge win. The final image is ~33% smaller and all I did was switch the base image and then spent 5 minutes learning how to use Alpine’s package manager.

That’s only the beginning too because we can shrink it down by an additional ~260% by introducing a few tricks into our Dockerfile.

Phase 1 Dockerfile comparison

Phase 1 is a direct comparison of Debian Jessie (Slim) vs Alpine with no additional tricks to minimize the final image size.

The Slim version

FROM python:2.7-slim
MAINTAINER Nick Janetakis <>

RUN apt-get update && apt-get install -qq -y \
  build-essential libpq-dev libffi-dev --no-install-recommends



COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

COPY . .

CMD gunicorn -b --access-logfile - ""

The Alpine version

FROM python:2.7-alpine
MAINTAINER Nick Janetakis <>

RUN apk update && apk add build-base postgresql-dev libffi-dev



COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt

COPY . .

CMD gunicorn -b --access-logfile - ""

In this case, I need libffi-dev to compile a bcrypt package dependency and libpq-dev is for postgresql.

Phase 2 Alpine based Dockerfile

At the moment, the Alpine version is 33% smaller but what happens when someone with more knowledge on Alpine than myself looks at it for a few minutes?

That’s what happened today when Natanael Copa reached out to me on Twitter and gisted a new version of the Dockerfile.

FROM python:2.7-alpine
MAINTAINER Nick Janetakis <>



COPY requirements.txt requirements.txt
RUN apk add --no-cache --virtual .build-deps \
  build-base postgresql-dev libffi-dev \
    && pip install -r requirements.txt \
    && find /usr/local \
        \( -type d -a -name test -o -name tests \) \
        -o \( -type f -a -name '*.pyc' -o -name '*.pyo' \) \
        -exec rm -rf '{}' + \
    && runDeps="$( \
        scanelf --needed --nobanner --recursive /usr/local \
                | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
                | sort -u \
                | xargs -r apk info --installed \
                | sort -u \
    )" \
    && apk add --virtual .rundeps $runDeps \
    && apk del .build-deps

COPY . .

CMD gunicorn -b --access-logfile - ""

The above Dockerfile dropped the final image size from 309.1MB to 117.3MB.

That’s ~260% smaller than the original Alpine image. It’s not fair to compare it against the Debian Jessie (Slim) version because we’re not cleaning up our compile-time dependencies, but it’s ~400% smaller as is.

Sign up for a free Codeship Account

What’s the catch?

The biggest downside is every time you change a package in your application, it will have to run through the entire apk add command which means all of the system dependencies will be re-installed.

This is going to add a few minutes to your build times, but remember – this increase only happens when you change your app’s package dependencies.

For typical code changes which is what 99% of your changes will be, the build time will be nearly identical with both versions.

Is it worth the occasional build time increases to ship around a 117MB image instead of one that’s over 300MB? That’s for you to decide, but I think it is.

Is Alpine worth it?

I’m very happy with Alpine and will certainly be updating all of my projects to use it.You can also expect the official Docker Hub versions of many images to support it in the near future. Redis already has an Alpine version and it’s only ~16MB.

Where to Go Next?

If you would like to learn more about Docker and how to deploy web applications to production in an automated way, you can follow the link below to get a 20% discount on Nick’s course Docker for DevOps: From development to production.

Subscribe via Email

Over 60,000 people from companies like Netflix, Apple, Spotify and O'Reilly are reading our articles.
Subscribe to receive a weekly newsletter with articles around Continuous Integration, Docker, and software development best practices.

We promise that we won't spam you. You can unsubscribe any time.

Join the Discussion

Leave us some comments on what you think about this topic or if you like to add something.

  • Fabio Pugliese Ornellas

    The catch is that Alpine does not use GNU’s libc, being this the main cause of its small size. Alternative libc implementation usually works, however, they are not the usual development environment, neither usually the fastest. There’s some risk involved (probably small).

    • That is true, but honestly I haven’t came across any abnormalities in production. I’ve been using it on a few projects now since they were made available.

      It’s worth noting that if you’re using the Docker Toolbox on OSX or Windows you may run into file syncing issues with Alpine based images. Here’s an issue that goes into that in more detail:

      • Fabio Pugliese Ornellas

        That’s the thing. I tested Alpine some time ago. It worked just fine. However, I do not have a massive issue (yet) with Docker Image sizes and gave it up due to this sort of risk (untested environment). When we exhaust our Registry space, we have plans to use a safe but aggressive old images purge routine (eg: keep latest 10 deliveries and latest month at least).

  • Mark Kubacki

    You could split the creation of said exemplary »real world application« and its runtime environment into two Docker images. That way you don’t need to worry about anything building leaves behind (test data? aux files?…), or even install/uninstall the build tools within your then bloated Docker image — and you actually freeze the build environment for later audits.

    The result of the building is called »artifact(s)«. The process is a »pipeline« or part of a pipeline. In a nutshell, you’d:

    1. Write a `Dockerfile` which results in your build environment. That doesn’t change.
    2. Write another `Dockerfile` which is the runtime environment (I just use a *base image*) and in which you install (or download, or ADD) the build artifacts.
    3. Write a »script« that, given (1), compiles your application and creates a tarball (or any other archive, such as a **deb** file).

    All you have to do is then run the script (3) in the build environment (1), get the artifacts, and create a new runtime environment (2).

    docker run –rm -t -v /source:/source:ro -v /release:/release –tmpfs /tmp
    docker run –privileged –rm -t -v /var/run/docker.sock:/var/run/docker.sock
    -v /release:/release
    docker /bin/sh -c “cd /release; docker build -t “…” .

    Or more elegantly, by running Docker in Docker, and utilizing Docker’s *build_arg* parameter for serving the artifacts from a ephemeral webserver instance:
    (The above example compiles PHP out-of-Dockerfile, and results in Docker images with PHP of less than 120 MB (the »official« images are at 370 MB).)

  • LDG

    Are there any performance concerns with Alpine in production? (Besides the libc/compile issues, i.e., while the app is actually running.)

    • Nope. It’s really solid, at least from what I have seen after having used it on many apps.

      Most of the popular official images all support Alpine now too.

  • Augustine Correa

    Also looking at Docker best practices, perhaps the app should be running under a non root user. Funnily I dont see this in most Dockerfiles in the wild and its more rarer with an alpine based image in the Dockerfile.

  • Tempestas Ludi

    When talking about a change, for example, something being smaller, you should use the unchanged version (the larger one) as a base for percentages. Saying that something is “200% smaller” sounds ridiculous, as it suggests that the size becomes negative…