Sometimes when I’m working with docker, I’ll have a frustrating experience. I want to do something simple, like copy a shell script into the container and execute it as the container’s entry point, but I miss a silly detail and get stuck for a while. The other day I was trying to run a dockerized Redis instance, and I kept running into a silly permissions issue with the file system that I just couldn’t figure out. In retrospect, as is so often the case in software, I was testing elaborate hypotheses and not checking something obvious, but in case this post saves someone else the trouble, I wanted to go into a little detail. Here’s how to make a redis container that starts with a shell script instead of just starting up the redis server right away.
I started out with a Dockerfile, like you do:
# not the working file yet FROM redis COPY start.sh /data/start.sh USER redis CMD ["/data/start.sh"]
Then I built the image:
docker build -f dockerfiles/Dockerfile.redis .
Then, I tried to run the image:
$ docker run <some hex string> /usr/local/bin/docker-entrypoint.sh: 16: exec: /data/start.sh: Permission denied
What? Why can’t docker execute this file.
Here in the sad story I started looking at a million different variables. Do I need to change user to root in the dockerfile? Do I need to
chown the script? Do I need to switch to a different container solution? Is there a bug in the universe?
Nope. This is a classic example of reporting a more specific error than was actually raised. What does “Permission denied” mean? Usually it means one of two things. Either I tried to execute a file that wasn’t executable, or I tried to read or write a file that wasn’t readable or writeable by the current user. So of course, that’s where all my investigation went. Is the file executable? Can the current user execute it? After a miserable, brief time of filling my dockerfile with pointless
USER‘s, and talking with a friend about this, I realized that all of this is predicated on something: All these tests assume the file exists.
So I changed my dockerfile to look like this:
# not the working file yet FROM redis COPY bin/start.sh /data/start.sh USER redis CMD ["cat /data/start.sh"]
And sure enough:
$ docker run 069eda169f4c /usr/local/bin/docker-entrypoint.sh: 16: exec: cat /data/start.sh: not found
Here’s where I went wrong. For some reason, I think because I’d seen a
/data directory in a redis container one time, I should put the shell script there. In retrospect, this was pretty foolish. Redis is using the
/data directory, well, probably for data. The redis startup process is mounting whatever it cares about right over top of my shell script.
I think I did two things wrong here: First, I made a completely arbitrary decision to put a script somewhere it didn’t belong; that’s the sort of thing I should think through. Second, I trusted the message in the error too much. I might prefer that trying to execute a shell script that doesn’t exist would return a different error than trying to execute a shell script with the wrong permissions. It turns out, once the file exists, you also need to set its permissions, but an ordinary
RUN chmod +x /start.sh in the docker file will fix that. Here’s a dockerfile that works:
# this is the working file now FROM redis COPY start.sh / USER root RUN chmod +x /start.sh USER redis CMD /start.sh
Writing the contents of
/start.sh is another topic for another day.
Till next time, happy learning!