docker update, or how to incrementally build images without Dockerfile.

This handy little script covers an important missing feature in current docker's tool set.

I needed to build incrementally my images:

From a given image, I want full script control (un-tamed shell scripts) and access to hosts files to do whatever I want to do. At the end, I need to commit the filesystem (and only the filesystem).

docker-update manage all this:

docker-update my-docker-image -v /srv/files:/mnt/files <<EOF

## Full fledged shell script, will run in docker container
## have access to /mnt/files

cp /mnt/files/data /opt/myapp/data

...

EOF

A last thing about the docker-update script, is that it tries to be clever (I know that's often the beginning of hell): if you apply the same code to the same image ID, it won't execute it, but will use the previous result instead.

You can disable the cache by inserting # docker: ALWAYS in your bash code.

You can look at the source code of docker-update, or directly download docker-update.

How does it work

It use docker run --entrypoint /bin/bash IMAGE on the bash script, then docker commit.

Well that's the general outline. In the detail, docker current shortcomings are making this idea much more complex than it should.

The first docker run will modify ENTRYPOINT and CMD of image, so when you commit, you break your previous values. So docker-update needs to save them, and push another commit to set them back.

docker-update shortcomings

As a quick and dirty solution, this script has some shortcomings and sharp corners. Some of these could easily repaired, and additions are welcome.

NUL characters in your code

Any NUL character from your bash script will be removed prior to be run. That's a direct consequence of running bash -c "$code". There are probably better ways to do this.

Complex CMD and ENTRYPOINT

I rely on the fact that the output format of {{json .Config.Cmd}} templating system is directly evaluable in the Dockerfile as arguments to CMD and ENTRYPOINT.

I'm pretty sure that this is bad. But it works in simple cases.

Caching

Remember that by default, your code will be checked wether it was already run on the same image (identified by it's ID). If you don't want that to happen, include # docker: ALWAYS.

For instance:

docker-update MYIMAGE <<EOF
# docker: always
apt-get update && apt-get upgrade -y
EOF

docker-update options

They are sent directly to docker run command, and you can easily break the command. It's primarily meant to be used with the -v option.