dockerfilesizechowndockerfile

Why does chown increase size of docker image?


I can't understand why the 'chown' command should increase the size of my docker image?

The following Dockerfile creates an image of size 5.3MB:

FROM alpine:edge
RUN adduser example -D -h /example -s /bin/sh

This example however creates an image of size 8.7MB:

FROM alpine:edge
RUN adduser example -D -h /example -s /bin/sh && \
    chown -R example.example /lib

Why?

Note: My actual dockerfile is of course much longer than this example and therefore the increase in image size is also quite larger. That's why I even care..


Solution

  • Every step in a Dockerfile generates a new intermediate image, or "layer", consisting of anything that changed on the filesystem from the previous layer. A docker image consists of a collection of layers that are applied one on top of another to create the final filesystem.

    If you have:

    RUN adduser example -D -h /example -s /bin/sh
    

    Then you are probably changing nothing other than a few files in /etc (/etc/passwd, /etc/group, and their shadow equivalents).

    If you have:

    RUN adduser example -D -h /example -s /bin/sh && \
        chown -R example.example /lib
    

    Then the list of things that have changed includes, recursively, everything in /lib, which is potentially larger. In fact, in my alpine:edge container, it looks like the contents of /lib is 3.4MB:

    / # du -sh /lib
    3.4M    /lib
    

    Which exactly accounts for the change in image size in your example.

    UPDATE

    Using your actual Dockerfile, with the npm install ... line commented out, I don't see any difference in the final image size whether or not the adduser and chown commands are run. Given:

    RUN echo "http://nl.alpinelinux.org/alpine/edge/main" > /etc/apk/repositories && \
        echo "http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \
        apk add -U wget iojs && \
        apk upgrade && \
        wget -q --no-check-certificate https://ghost.org/zip/ghost-0.6.0.zip -O /tmp/ghost.zip && \
        unzip -q /tmp/ghost.zip -d /ghost && \
        cd /ghost && \
    #    npm install --production && \
        sed 's/127.0.0.1/0.0.0.0/' /ghost/config.example.js > /ghost/config.js && \
        sed -i 's/"iojs": "~1.2.0"/"iojs": "~1.6.4"/' package.json && \
    #   adduser ghost -D -h /ghost -s /bin/sh && \
    #   chown -R ghost.ghost * && \
        npm cache clean && \
        rm -rf /var/cache/apk/* /tmp/*
    

    I get:

    $ docker build -t sotest .
    [...]
    Successfully built 058d9f41988a
    $ docker inspect -f '{{.VirtualSize}}' 058d9f41988a
    31783340
    

    Whereas given:

    RUN echo "http://nl.alpinelinux.org/alpine/edge/main" > /etc/apk/repositories && \
        echo "http://nl.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories && \
        apk add -U wget iojs && \
        apk upgrade && \
        wget -q --no-check-certificate https://ghost.org/zip/ghost-0.6.0.zip -O /tmp/ghost.zip && \
        unzip -q /tmp/ghost.zip -d /ghost && \
        cd /ghost && \
    #    npm install --production && \
        sed 's/127.0.0.1/0.0.0.0/' /ghost/config.example.js > /ghost/config.js && \
        sed -i 's/"iojs": "~1.2.0"/"iojs": "~1.6.4"/' package.json && \
        adduser ghost -D -h /ghost -s /bin/sh && \
        chown -R ghost.ghost * && \
        npm cache clean && \
        rm -rf /var/cache/apk/* /tmp/*
    

    I get:

    $ docker build -t sotest .
    [...]
    Successfully built 696b481c5790
    $ docker inspect -f '{{.VirtualSize}}' 696b481c5790
    31789262
    

    That is, the two images are approximately the same size (the difference is around 5 Kb).

    I would of course expect the resulting image to be larger if the npm install command could run successfully (because that would install additional files).