How to generate a Dockerfile from an image?

Is it possible to generate a Dockerfile from an image? I want to know for two reasons:

  1. I can download images from the repository but would like to see the recipe that generated them.

  2. I like the idea of saving snapshots, but once I am done it would be nice to have a structured format to review what was done.

1

10 Answers

How to generate or reverse a Dockerfile from an image?

You can. Mostly.

Notes: It does not generate a Dockerfile that you can use directly with docker build; the output is just for your reference.

alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm alpine/dfimage" dfimage -sV=1.36 nginx:latest 

It will pull the target docker image automatically and export Dockerfile. Parameter -sV=1.36 is not always required.

Reference:

Now hub.docker.com shows the image layers with detail commands directly, if you choose a particular tag.

enter image description here

Bonus

If you want to know which files are changed in each layer

alias dive="docker run -ti --rm -v /var/run/docker.sock:/var/run/docker.sock wagoodman/dive" dive nginx:latest 

enter image description here

On the left, you see each layer's command, on the right (jump with tab), the yellow line is the folder that some files are changed in that layer

(Use SPACE to collapse dir)

Old answer

below is the old answer, it doesn't work any more.

$ docker pull centurylink/dockerfile-from-image $ alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm centurylink/dockerfile-from-image" $ dfimage --help Usage: dockerfile-from-image.rb [options] <image_id> -f, --full-tree Generate Dockerfile for all parent layers -h, --help Show this message 
14

To understand how a docker image was built, use the docker history --no-trunc command.

You can build a docker file from an image, but it will not contain everything you would want to fully understand how the image was generated. Reasonably what you can extract is the MAINTAINER, ENV, EXPOSE, VOLUME, WORKDIR, ENTRYPOINT, CMD, and ONBUILD parts of the dockerfile.

The following script should work for you:

#!/bin/bash docker history --no-trunc "$1" | \ sed -n -e 's,.*/bin/sh -c #(nop) \(MAINTAINER .*[^ ]\) *0 B,\1,p' | \ head -1 docker inspect --format='{{range $e := .Config.Env}} ENV {{$e}} {{end}}{{range $e,$v := .Config.ExposedPorts}} EXPOSE {{$e}} {{end}}{{range $e,$v := .Config.Volumes}} VOLUME {{$e}} {{end}}{{with .Config.User}}USER {{.}}{{end}} {{with .Config.WorkingDir}}WORKDIR {{.}}{{end}} {{with .Config.Entrypoint}}ENTRYPOINT {{json .}}{{end}} {{with .Config.Cmd}}CMD {{json .}}{{end}} {{with .Config.OnBuild}}ONBUILD {{json .}}{{end}}' "$1" 

I use this as part of a script to rebuild running containers as images:

The Dockerfile is mainly useful if you want to be able to repackage an image.

The thing to keep in mind, is a docker image can actually just be the tar backup of a real or virtual machine. I have made several docker images this way. Even the build history shows me importing a huge tar file as the first step in creating the image...

3

I somehow absolutely missed the actual command in the accepted answer, so here it is again, bit more visible in its own paragraph, to see how many people are like me

$ docker history --no-trunc <IMAGE_ID> 
8

A bash solution :

docker history --no-trunc $argv | tac | tr -s ' ' | cut -d " " -f 5- | sed 's,^/bin/sh -c #(nop) ,,g' | sed 's,^/bin/sh -c,RUN,g' | sed 's, && ,\n & ,g' | sed 's,\s*[0-9]*[\.]*[0-9]*\s*[kMG]*B\s*$,,g' | head -n -1 

Step by step explanations:

tac : reverse the file tr -s ' ' trim multiple whitespaces into 1 cut -d " " -f 5- remove the first fields (until X months/years ago) sed 's,^/bin/sh -c #(nop) ,,g' remove /bin/sh calls for ENV,LABEL... sed 's,^/bin/sh -c,RUN,g' remove /bin/sh calls for RUN sed 's, && ,\n & ,g' pretty print multi command lines following Docker best practices sed 's,\s*[0-9]*[\.]*[0-9]*\s*[kMG]*B\s*$,,g' remove layer size information head -n -1 remove last line ("SIZE COMMENT" in this case) 

Example:

 ~  dih ubuntu:18.04 ADD file:28c0771e44ff530dba3f237024acc38e8ec9293d60f0e44c8c78536c12f13a0b in / RUN set -xe && echo '#!/bin/sh' > /usr/sbin/policy-rc.d && echo 'exit 101' >> /usr/sbin/policy-rc.d && chmod +x /usr/sbin/policy-rc.d && dpkg-divert --local --rename --add /sbin/initctl && cp -a /usr/sbin/policy-rc.d /sbin/initctl && sed -i 's/^exit.*/exit 0/' /sbin/initctl && echo 'force-unsafe-io' > /etc/dpkg/dpkg.cfg.d/docker-apt-speedup && echo 'DPkg::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };' > /etc/apt/apt.conf.d/docker-clean && echo 'APT::Update::Post-Invoke { "rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true"; };' >> /etc/apt/apt.conf.d/docker-clean && echo 'Dir::Cache::pkgcache ""; Dir::Cache::srcpkgcache "";' >> /etc/apt/apt.conf.d/docker-clean && echo 'Acquire::Languages "none";' > /etc/apt/apt.conf.d/docker-no-languages && echo 'Acquire::GzipIndexes "true"; Acquire::CompressionTypes::Order:: "gz";' > /etc/apt/apt.conf.d/docker-gzip-indexes && echo 'Apt::AutoRemove::SuggestsImportant "false";' > /etc/apt/apt.conf.d/docker-autoremove-suggests RUN rm -rf /var/lib/apt/lists/* RUN sed -i 's/^#\s*\(deb.*universe\)$/\1/g' /etc/apt/sources.list RUN mkdir -p /run/systemd && echo 'docker' > /run/systemd/container CMD ["/bin/bash"] 
6

Update Dec 2018 to BMW's answer

chenzj/dfimage - as described on hub.docker.com regenerates Dockerfile from other images. So you can use it as follows:

docker pull chenzj/dfimage alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm chenzj/dfimage" dfimage IMAGE_ID > Dockerfile 
3

This is derived from @fallino's answer, with some adjustments and simplifications by using the output format option for docker history. Since macOS and Gnu/Linux have different command-line utilities, a different version is necessary for Mac. If you only need one or the other, you can just use those lines.

#!/bin/bash case "$OSTYPE" in linux*) docker history --no-trunc --format "{{.CreatedBy}}" $1 | # extract information from layers tac | # reverse the file sed 's,^\(|3.*\)\?/bin/\(ba\)\?sh -c,RUN,' | # change /bin/(ba)?sh calls to RUN sed 's,^RUN #(nop) *,,' | # remove RUN #(nop) calls for ENV,LABEL... sed 's, *&& *, \\\n \&\& ,g' # pretty print multi command lines following Docker best practices ;; darwin*) docker history --no-trunc --format "{{.CreatedBy}}" $1 | # extract information from layers tail -r | # reverse the file sed -E 's,^(\|3.*)?/bin/(ba)?sh -c,RUN,' | # change /bin/(ba)?sh calls to RUN sed 's,^RUN #(nop) *,,' | # remove RUN #(nop) calls for ENV,LABEL... sed $'s, *&& *, \\\ \\\n \&\& ,g' # pretty print multi command lines following Docker best practices ;; *) echo "unknown OSTYPE: $OSTYPE" ;; esac 

It is not possible at this point (unless the author of the image explicitly included the Dockerfile).

However, it is definitely something useful! There are two things that will help to obtain this feature.

  1. Trusted builds (detailed in this docker-dev discussion
  2. More detailed metadata in the successive images produced by the build process. In the long run, the metadata should indicate which build command produced the image, which means that it will be possible to reconstruct the Dockerfile from a sequence of images.
docker pull chenzj/dfimage alias dfimage="docker run -v /var/run/docker.sock:/var/run/docker.sock --rm chenzj/dfimage" dfimage image_id

Below is ouput of dfimage command:

$ dfimage 0f1947a021ce FROM node:8 WORKDIR /usr/src/app COPY file:e76d2e84545dedbe901b7b7b0c8d2c9733baa07cc821054efec48f623e29218c in ./ RUN /bin/sh -c npm install COPY dir:a89a4894689a38cbf3895fdc0870878272bb9e09268149a87a6974a274b2184a in . EXPOSE 8080 CMD ["npm" "start"] 

If you are interested in an image that is in the Docker hub registry and wanted to take a look at Dockerfile?.

Example:

If you want to see the Dockerfile of image "jupyter/datascience-notebook" type the word "Dockerfile" in the address bar of your browser as shown below.

enter image description here

enter image description here

Note: Not all the images have Dockerfile, for example, Sometimes this way is much faster than searching for Dockerfile in Github.

What is image2df

image2df is tool for Generate Dockerfile by an image.

This tool is very useful when you only have docker image and need to generate a Dockerfile whit it.

How does it work

Reverse parsing by history information of an image.

How to use this image

# Command alias echo "alias image2df='docker run -v /var/run/docker.sock:/var/run/docker.sock --rm cucker/image2df'" >> ~/.bashrc . ~/.bashrc # Excute command image2df <IMAGE> 
  • See help

    docker run --rm cucker/image2df --help 
  • For example

    $ echo "alias image2df='docker run -v /var/run/docker.sock:/var/run/docker.sock --rm cucker/image2df'" >> ~/.bashrc $ . ~/.bashrc $ docker pull mysql $ image2df mysql ========== Dockerfile ========== FROM mysql:latest RUN groupadd -r mysql && useradd -r -g mysql mysql RUN apt-get update && apt-get install -y --no-install-recommends gnupg dirmngr && rm -rf /var/lib/apt/lists/* ENV GOSU_VERSION=1.12 RUN set -eux; \ savedAptMark="$(apt-mark showmanual)"; \ apt-get update; \ apt-get install -y --no-install-recommends ca-certificates wget; \ rm -rf /var/lib/apt/lists/*; \ dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \ wget -O /usr/local/bin/gosu ""; \ wget -O /usr/local/bin/gosu.asc ""; \ export GNUPGHOME="$(mktemp -d)"; \ gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \ gpgconf --kill all; \ rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc; \ apt-mark auto '.*' > /dev/null; \ [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark > /dev/null; \ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ chmod +x /usr/local/bin/gosu; \ gosu --version; \ gosu nobody true RUN mkdir /docker-entrypoint-initdb.d RUN apt-get update && apt-get install -y --no-install-recommends \ pwgen \ openssl \ perl \ xz-utils \ && rm -rf /var/lib/apt/lists/* RUN set -ex; \ key='A4A9406876FCBD3C456770C88C718D3B5072E1F5'; \ export GNUPGHOME="$(mktemp -d)"; \ gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \ gpg --batch --export "$key" > /etc/apt/trusted.gpg.d/mysql.gpg; \ gpgconf --kill all; \ rm -rf "$GNUPGHOME"; \ apt-key list > /dev/null ENV MYSQL_MAJOR=8.0 ENV MYSQL_VERSION=8.0.24-1debian10 RUN echo 'deb buster mysql-8.0' > /etc/apt/sources.list.d/mysql.list RUN { \ echo mysql-community-server mysql-community-server/data-dir select ''; \ echo mysql-community-server mysql-community-server/root-pass password ''; \ echo mysql-community-server mysql-community-server/re-root-pass password ''; \ echo mysql-community-server mysql-community-server/remove-test-db select false; \ } | debconf-set-selections \ && apt-get update \ && apt-get install -y \ mysql-community-client="${MYSQL_VERSION}" \ mysql-community-server-core="${MYSQL_VERSION}" \ && rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql /var/run/mysqld \ && chown -R mysql:mysql /var/lib/mysql /var/run/mysqld \ && chmod 1777 /var/run/mysqld /var/lib/mysql VOLUME [/var/lib/mysql] COPY dir:2e040acc386ebd23b8571951a51e6cb93647df091bc26159b8c757ef82b3fcda in /etc/mysql/ COPY file:345a22fe55d3e6783a17075612415413487e7dba27fbf1000a67c7870364b739 in /usr/local/bin/ RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat ENTRYPOINT ["docker-entrypoint.sh"] EXPOSE 3306 33060 CMD ["mysqld"] 
  • reference

2

You Might Also Like