Troubleshooting a basic docker issue: Getting a script into the container

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 chmod or 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 chmod‘s and chown‘s and 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!

-Will

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s