published on in Docker
tags: alpine docker linux ubuntu

Docker, why I prefer Alpine as base instead of Ubuntu

I like Docker. I use it almost everything. A few months ago, I started every Dockerfile with this line:

FROM ubuntu:xx.xx

where xx.xx is the latest LTS version. Ok nice. After I used Docker I realised, the size and time matter. I started to seek a new base image that fits my needs and smaller (quicker build). I found Alpine. It’s awesome.

Here is an ubuntu based Dockerfile to run my sample Go project:

FROM ubuntu

MAINTAINER Balazs Nadasdi <balazs.nadasdi@cheppers.com>

# Update package index and install go + git
RUN apt-get update -q \
    && apt-get install -yq golang git-core

# Set up GOPATH
RUN mkdir /go
ENV GOPATH /go

# Get dependencies
RUN go get gopkg.in/mgo.v2 && \
    go get github.com/go-martini/martini && \
    go get gopkg.in/redis.v3

# Add current working directory
ADD . /go/src/github.com/Yitsushi/livetogether

# Build it
RUN go install github.com/Yitsushi/livetogether

# Every time I start the container I want to rebuild
# because I mount it when I use it for development
ENTRYPOINT go install github.com/Yitsushi/livetogether && \
           /go/bin/livetogether \
           --config=/go/src/github.com/Yitsushi/livetogether/config.json

# Expose where the application wants to listen
EXPOSE 8080

And now see with Alpine

FROM alpine

MAINTAINER Balazs Nadasdi <balazs.nadasdi@cheppers.com>

# Update package index and install go + git
RUN apk add --update go git

# Set up GOPATH
RUN mkdir /go
ENV GOPATH /go

# Get dependencies
RUN go get gopkg.in/mgo.v2 && \
    go get github.com/go-martini/martini && \
    go get gopkg.in/redis.v3

# Add current working directory
ADD . /go/src/github.com/Yitsushi/livetogether

# Build it
RUN go install github.com/Yitsushi/livetogether

# Every time I start the container I want to rebuild
# because I mount it when I use it for development
ENTRYPOINT go install github.com/Yitsushi/livetogether && \
           /go/bin/livetogether \
           --config=/go/src/github.com/Yitsushi/livetogether/config.json

# Expose where the application wants to listen
EXPOSE 8080

And now see our base images

❯ docker images | awk '{print $1"\t"$2"\t"$7" "$8}'
REPOSITORY  TAG SIZE
ubuntu  latest  187.9 MB
redis   latest  109.3 MB
mongo   latest  261.6 MB
golang  latest  709.5 MB
alpine  latest  5.249 MB

Build time?

❯ time docker build -t sample_ubuntu livetogetheru/
...
docker build -t sample_ubuntu livetogetheru  0.17s user 0.03s system 0% cpu 6:22.66 total
❯ time docker build -t sample_alpine livetogether/
...
docker build -t sample_alpine livetogether  0.15s user 0.01s system 0% cpu 2:09.89 total

the point:

ubuntu: 6:22.66 total
alpine: 2:09.89 total

Wow… and after the build?

❯ docker images | awk '{print $1"\t"$2"\t"$7" "$8}'
REPOSITORY  TAG SIZE
sample_alpine   latest  167.6 MB
sample_ubuntu   latest  447.8 MB
ubuntu  latest  187.9 MB
redis   latest  109.3 MB
mongo   latest  261.6 MB
golang  latest  709.5 MB
alpine  latest  5.249 MB

What? 3 times more time to build and 4 times bigger the app image size and more than 30 times bigger the base image? omg!

(as you see, don’t use the base golang image because it’s much more bigger and you don’t need it)