Linux Permissions 101

To work with Docker is to work with Unix permissions.

There are unintuitive relationships between running containers and the underlying hosts physical storage.

You will run containers that save their data to your computer's hard disk.

As far as the applications we have running inside our Docker containers are concerned, the location they read/write from/to is entirely isolated from the location we are really storing that information in on our development workstation.

Let's imagine we currently run a full copy of Ubuntu server, running inside Virtual Box, that we run locally on our development workstation whenever we work on a particular project.

This is good as it keeps our system's infrastructure isolated from our development workstation. We can use tools like Ansible to ensure this Virtual Machine is able to be reliably re-provisioned for yourself, and others on your team.

In this instance when we run e.g. nginx, the paths the OS uses are the actual paths on disk:

  • /var/log/nginx
  • /etc/nginx/conf.d

These are locations that nginx expects to read from, and write too.

In order to read and write, permissions are required.

As this is a Unix-based system, Unix permissions apply.

You will very likely have experienced these types of permissions, even if you've never truly understood what they mean:

➜  ~ ls -la Development/docker/nginx

total 24
drwxrwxr-x  4 www-data www-data 4096 Apr  3 19:43 .
drwxrwxr-x 74 chris    chris    4096 Aug 17 20:15 ..
drwxrwxr-x  2 www-data www-data 4096 Apr  3 19:37 conf.d
-rwxrwxr-x  1 www-data www-data  137 Apr 18 20:51 Dockerfile
-rwxrwxr-x  1 www-data www-data   13 Apr  3 19:42 .dockerignore
drwxrwxr-x  8 www-data www-data 4096 Aug 19 12:05 .git

There's a lot going on here, so let's break it down.

ls -la Development/docker/nginx

ls lists the directory contents.

-la - long listing format, all files

Useless trivia: total refers to the total disk allocation in blocks, for all the files in the directory.

drwxrwxr-x is where things start to get interesting and meaningful for us.

The output here tells us a bunch of really useful information.

The first character is typically either d, or -. There are other characters, but these are by far the most common.

If it's d, it means this is a Directory.

If it's a -, it's a file.

Next, we need to split the characters into three groups of three:



  • rwx
  • rwx
  • r-x

These are the permissions for:

  • user
  • group
  • everyone

The letters stand for:

  • r - Read
  • w - Write
  • x - eXecute

The letters only ever appear in the same spots. Three groups of three, with either a specific letter (r, w, or x) or a dash.

I think of them as toggles.

If you have the right letter, you get that permission.

You may wonder what having x (execute) permissions to a directory means: it simply means you have permission to cd or change directory into that directory.

These letters also have a numeric value:

  • r has the value of 4;
  • w has the value of 2;
  • x has the value of 1

By combining the permissions and adding the three together, you get the numeric representation of the permission:

644 becomes:

6 = rw- for user 4 = r-- from group 4 = r-- from everyone

755 becomes:

7 = rwx for user 5 = r-x from group 5 = r-x from everyone

777 becomes:

7 = rwx for user 7 = rwx for group 7 = rwx for everyone

And you can hopefully see now why if you have ever had to chmod 777 a directory to make something work, that is a really bad thing to have to do.

600 becomes:

6 = rw- for user 0 = --- from group 0 = --- from everyone

In this case only the user can read and write. This is common for security keys, and things of that nature.


drwxrwxr-x 4 www-data www-data 4096 Apr 3 19:43 .

The next number 4 is the link count. This is not relevant to our purposes. If you'd like to know more, read this StackOverflow post.

We do care about the next two columns.

www-data www-data

Here we learn the user, and group, respectively.

It is unintuitive when a user and a group share the same name.

Here the www-data is both the name of a user, and the name of a group in our system user / groups list.

To add to the confusion, the user www-data is a member of the www-data group :)

Anyway, we now know who the three groups of permissions affect:

This is a directory, with the permissions:

  • rwx for user, where user is www-data.
  • rwx for group, where group is www-data.
  • r-x for everyone else.

So long as we are either authenticated as the user www-data, or we are authenticated as some other user who happens to be a member of the www-data group, we can read, write, and execute on this particular directory.

If we are neither www-data, nor a user who is a member of the www-data group then we can only read, or execute. We cannot write new data here.

We could numerically represent these permissions as 775, or 0775. The leading 0 is the preferred pattern, and the one we will use as needed in this series. The reasons will satisfy your inner nerd.

The number 4096 is the size of the resource this line applies too. More on that in a moment.

You can find out why directories are always a size of `4096 here. Again, curiosity stuff, not essential.

The next part: Apr 3 19:43, is a timestamp :)

This represents the date the file was last modified. The concept of "created at" is hazy on Unix-like systems.


➜  ~ ls -la Development/docker/nginx

total 24
drwxrwxr-x  4 www-data www-data 4096 Apr  3 19:43 .
drwxrwxr-x 74 chris    chris    4096 Aug 17 20:15 ..
drwxrwxr-x  2 www-data www-data 4096 Apr  3 19:37 conf.d
-rwxrwxr-x  1 www-data www-data  137 Apr 18 20:51 Dockerfile
-rwxrwxr-x  1 www-data www-data   13 Apr  3 19:42 .dockerignore
drwxrwxr-x  8 www-data www-data 4096 Aug 19 12:05 .git

Lastly, here we have either:

  • .
  • ..
  • {name}

The {name} above should be fairly self explanatory. Examples here are conf.d, Dockerfile, .git etc.

Trivia: We see the .git directory because the a part of ls -la shows us 'all', including hidden files. Folders starting with a . are hidden by default.

The more confusing are:

  • .
  • ..

The . represents the current directory.

You can use this as a shorthand in many common command line tasks.

Let's say you log into a server running a Symfony website and for whatever reason you'd like to copy the prod.log file from the live site dir, to your current directory:

$ cd /tmp/some-path
cp /path/to/my/symfony-site/var/logs/prod.log .

# equivalent to:

cp /path/to/my/symfony-site/var/logs/prod.log /tmp/some-path

We will see this single period notation used again in our docker-compose.yml files.

The double period .. notation indicates up one directory. Our directories parent.

In other words, if we go up one directory, that directory is owned by user chris, and the group of chris.

You may remember this notation from Symfony 3.2 and earlier:

# app/config/services.yml

        class: AppBundle\Service\SomeImportantService
            - "%kernel.root_dir%/../web/images"

Whereby we used the Symfony parameter %kernel.root_dir% to represent the location in which Symfony is installed. However, this kernel.root_dir parameter will expand out to our project's dir, and then down one directory in to app, where the AppKernel.php file resides.

We would then need to use the double period /../ to traverse up, and out of the directory and back to the root. From there we can browse in a more intuitive fashion.

As a side note they fixed this quirk in Symfony 3.3 by including the kernel.project_dir which does point the expected root location of your project.

Code For This Course

Get the code for this course.