I stumbled upon this when I was containerizing a proprietary web app we're using, in my Dockerfile I copied the app with COPY and chowned it in RUN, wasn't happy with image size, then I found COPY --chown parameter, with it the size of an image was smaller by the size of the directory I was COPYing, of course I know every RUN creates a layer containing all changes made inside of it, kinda like Copy On Write mechanism, but I didn't realized a simple change of file properties like owner makes a whole new copy
**EXAMPLE:**
FROM alpine:latest AS downloader
WORKDIR /app
RUN wget -q -O- http://www.example.com/app.zip | \
unzip -q -
FROM php:7.2-apache
COPY --chown=www-data:www-data --from=downloader /app /var/www/html/
# build time dependencies needed to build required PHP extensions
ENV EXT_BUILD_DEPS libfreetype6-dev libjpeg62-turbo-dev libpng-dev \
libxmlrpc-epi-dev libxmltok1-dev libxslt1-dev libssl-dev libz-dev libtidy-dev libc-client-dev \
librecode-dev libmariadb-dev libbz2-dev libpspell-dev libkrb5-dev libmemcached-dev libzip-dev \
libmagickwand-dev
# run time dependencies needed for PHP the required extensions to run
ENV EXT_RUNTIME_DEPS libpng16-16 libc-client2007e libmemcached11 libaspell15 librecode0 libxslt1.1 \
libzip4 libtidy5deb1 libjpeg62-turbo libmemcachedutil2 libfreetype6 libmagickwand-6.q16-6
RUN export PHP_EXT_DIR=$(php-config --extension-dir) \
&& curl -sSL https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz \
|tar zxv -C$PHP_EXT_DIR/ ioncube/ioncube_loader_lin_7.2.so --strip-components=1 \
&& mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
&& echo "zend_extension = $PHP_EXT_DIR/ioncube_loader_lin_7.2.so" >> "$PHP_INI_DIR/php.ini" \
&& apt-get update && apt-get install --no-install-recommends -y $EXT_BUILD_DEPS $EXT_RUNTIME_DEPS \
&& docker-php-ext-configure gd --with-freetype-dir --with-jpeg-dir \
&& docker-php-ext-install -j$(nproc) gd \
&& docker-php-ext-configure imap --with-imap-ssl --with-kerberos \
&& docker-php-ext-install -j$(nproc) imap \
&& docker-php-ext-install -j$(nproc) intl \
&& docker-php-ext-install -j$(nproc) mysqli \
&& docker-php-ext-install -j$(nproc) opcache \
&& docker-php-ext-install -j$(nproc) pspell \
&& docker-php-ext-install -j$(nproc) recode \
&& docker-php-ext-install -j$(nproc) tidy \
&& docker-php-ext-install -j$(nproc) xmlrpc \
&& docker-php-ext-install -j$(nproc) xsl \
&& docker-php-ext-configure zip --with-libzip \
&& docker-php-ext-install -j$(nproc) zip \
&& yes "" | pecl install memcached \
&& docker-php-ext-enable memcached \
&& yes "" | pecl install imagick \
&& docker-php-ext-enable imagick \
&& apt-get purge -y -f --force-yes $EXT_BUILD_DEPS \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
EXPOSE 80
in my case app size is around 160MB, an image is 600MB, with `chown www-data:www-data /var/www/html` in RUN instead as COPY option the image size is around 760MB
Thanks for the tip
Just a tip you could run copy then && and then chown that would result in one layer instead of 2
Every instruction in a `Dockerfile` creates a new layer, not just `RUN`. While it won’t reduce the size by much, you can reduce the number of layers by combining your ENV instructions in to one using quotes and separating by commas.
*cries in chmod*
As a side note, I see you’re performing a multi-stage build just to get the output of a zip file.
You could’ve just piped the zip file to an unzip into a directory with a one liner pipe without the zip file ever touching the disk.