Skip to content
Advertisement

How can one influence the order of RUN commands in a Dockerfile?

I’m writing a Dockerfile where the root user creates a user named blog to manage website deployment. I’m using the Docker Hub wordpress container as a base. The root user creates folders under /var/www/html and gives the blog user permissions to write underneath.

In the next set of RUN commands following USER blog, these commands do not acknowledge the previous state where blog user was given permission to write in /var/www/html. This user needs to clone a git repo in there, but I get the error fatal: could not create leading directories of '/var/www/html/wp-content/uploads': Permission denied due to the new user not being able to write there, despite setting permissions for this user previously.

Here are the commands I’m using to create the blog user, copy files to their home directory, and then clone a repo with this user:

ENV WORDPRESS_DB_USER=wp_blog 
WORDPRESS_DB_NAME=wp_blog 
WORDPRESS_DIR=/var/www/html 
TERM=xterm

# Setup the WordPress site user
# Preliminary command
RUN useradd --create-home --shell /bin/false --groups www-data blog    

COPY id_rsa* known_hosts /home/blog/.ssh/ 
## First set of commands
RUN mkdir --parents "$WORDPRESS_DIR"/wp-content/uploads 
&& chown --recursive blog:blog "$WORDPRESS_DIR" 
&& chown --recursive blog:blog /home/blog

USER blog 
## Second set of commands
RUN chmod u=rw,g=,o= /home/blog/.ssh/id_rsa 
&& chmod u=rw,g=r,o=r /home/blog/.ssh/id_rsa.pub 
&& chmod u=rw,g=r,o=r /home/blog/.ssh/known_hosts 
&& eval $(ssh-agent -s) 
&& ssh-add 
&& export PATH=$PATH:/usr/sbin 

# Use the WordPress user to download the content repo and hooks 
# The following command results in a permissions error:   
&& git clone git@gitlab.com:jb-merideoux/jbm-uploads.git "$WORDPRESS_DIR"/wp-content/uploads 
&& mkdir --parents /home/blog/git/wpgithooks 
&& cd /home/blog/git/wpgithooks 
&& git clone git@github.com:enderandpeter/wpgithooks.git /home/blog/git/wpgithooks 
&& git checkout --git-dir=/home/blog/git/wpgithooks/.git --track origin/wpaddons 
&& chmod u+x /home/blog/git/wpgithooks/*.sh 
&& echo Run the script at /home/blog/git/wpgithooks/setup.sh to get started

The blog user is able to set permissions for the copied SSH key pair, but it cannot if the chown --recursive blog:blog /home/blog command is not present. I would think that the previous chown --recursive blog:blog "$WORDPRESS_DIR" command would have an equal effect on whether or not the blog user can write in /var/www/html in the same set of commands where blog changes permissions for files copied to its home by root after the root user made sure everything under /home/blog was owned by blog. Somehow, the permissions for /home/blog are in effect in the second set of RUN commands, but not the permissions for /var/www/html.

Upon closer inspection, it would appear that the mkdir --parents "$WORDPRESS_DIR"/wp-content/uploads instruction has not run either because when I precede the git clone command with something to create a file like touch "$WORDPRESS_DIR"/wp-content/uploads/afile, the error is touch: cannot touch '/var/www/html/wp-content/uploads/afile': No such file or directory.

When I try to createafile in /var/www/html/ (which already exists because it was created by the base wordpress container), I get the error touch: cannot touch '/var/www/html/afile': Permission denied, indicating that the chown --recursive blog:blog "$WORDPRESS_DIR" command was not acknowledged. Of the three commands in the first set of RUN statements, it looks like the only one acknowledged by the second set of RUN statements is chown --recursive blog:blog /home/blog.

How might I write this Dockerfile so that all of the commands in the first RUN statement have been issued by the time the second set of RUN statements are executed by the new user?

Advertisement

Answer

Wow, that was intense. The second set of commands just never pick up on what was done beforehand. I’ve seen examples where subsequent RUN commands are aware of things that happened in previous ones, so I’m still not clear on just how that is determined.

The solution I found was to use su in the first set of commands, right after root creates directories and sets permissions:

RUN mkdir --parents "$WORDPRESS_DIR"/wp-content/uploads 
    && chown --recursive blog:blog "$WORDPRESS_DIR" 
    && chown --recursive blog:blog /home/blog 
    && su - blog -s /bin/bash -c '
        chmod u=rwx,g=,o= /home/blog/.ssh 
        && chmod u=rw,g=,o= /home/blog/.ssh/id_rsa 
...
    && git clone git@gitlab.com:jb-merideoux/jbm-uploads.git '"$WORDPRESS_DIR"'/wp-content/uploads 
...
    && echo '"$WORDPRES_DIR"' setup complete.'

As you can see, that is some insane quoting, but luckily you can format the commands exactly as though they were given straight to the Dockerfile. I did use USER blog at the very end to do things that didn’t depend on the earlier stuff, but I guess sometimes with Docker, you have to string a whole bunch of commands together, but I can probably get all this into a separate script…

Advertisement