SSD Nodes Learn 🎉 VPS from $4.99/mo
Guides Matt ConnorBy Matt Connor

drwxr-xr-x explained: Linux file permissions

Read drwxr-xr-x one character at a time and map it to 755. Learn why x on a directory means traverse, and why chmod 777 is never the right fix.

What drwxr-xr-x means

drwxr-xr-x describes a directory that its owner can change, and that every other user can read and pass through without changing anything. In octal that mode is 755. Linux prints these ten characters at the start of every line of ls -l output, and they always mean the same thing in the same order, so learning one string teaches you all of them.

One rule comes before the rest, because it decides whether your own testing tells you anything. The root user ignores permission bits. The kernel grants root the CAP_DAC_OVERRIDE capability (discretionary access control override), so root opens files whose mode forbids it. Every example on this page succeeds for root, whatever the bits say. Log in as a normal user when you want to watch the rules act.

The ten characters, one at a time

Make a directory and a file to look at. Nothing here touches anything outside the new directory.

mkdir -p ~/permdemo/inner
printf 'hello\n' > ~/permdemo/inner/notes.txt
ls -ld ~/permdemo ~/permdemo/inner ~/permdemo/inner/notes.txt

With the common default umask of 022, the two directory lines start with drwxr-xr-x and the file line starts with -rw-r--r--.

Character 1 is the file type, not a permission. d is a directory. - is a regular file. l is a symbolic link. c and b are character and block device nodes. s is a socket, and p is a named pipe. It sits outside the octal value, which is why drwxr-xr-x becomes 755 rather than something beginning with a d.

The nine characters after it are three groups of three, and they never change order.

  • Characters 2 to 4 are the owner triad, the bits that apply to the user who owns the file.
  • Characters 5 to 7 are the group triad, the bits that apply to the file's group.
  • Characters 8 to 10 are the other triad, the bits that apply to everybody else.

Inside a triad the slots are always r, then w, then x, and a dash means that bit is off. The letters never move position. r-x is read without write. -w- is write without read, which is legal and rare.

So drwxr-xr-x splits like this: d for directory, then rwx for the owner, then r-x for the group, then r-x for other.

Some systems print an eleventh character. A trailing dot, drwxr-xr-x., means the file carries an SELinux (security enhanced Linux) context, which SELinux distributions such as Fedora and Rocky print by default. A trailing plus, drwxr-xr-x+, means the file carries a POSIX ACL (access control list), an extra set of rules beyond these nine bits. Read those extra rules with getfacl <path>.

r, w and x do different jobs on a directory

This is the first rule beginners get wrong. The letters are the same on a file and on a directory. The powers they grant are not.

  • r on a file reads the contents. r on a directory lists the names inside it, which is what plain ls needs.
  • w on a file changes the contents. w on a directory adds and removes entries in it. Deleting a file is a change to the directory, so write on the directory is what decides it, and the mode of the file itself has no say.
  • x on a file runs it as a program. x on a directory traverses it, which means the kernel may resolve one name inside it during a path lookup.

Traverse is the part that surprises people. x on a directory executes nothing. Opening /srv/site/index.html needs x on /, then x on /srv, then x on /srv/site, and finally r on the file. If one directory in that chain lacks x for you, the lookup stops there, and the caller is told Permission denied for the whole path even when the file at the end is world readable. namei -l /srv/site/index.html prints every step of the chain with its mode and owner, so you can see which link stops it.

A directory with r and no x is a strange half state worth recognising. A normal user can list the names, because listing is what r grants, but cannot stat any entry, so ls -l fills the size and mode columns with question marks and prints ls: cannot access ...: Permission denied for each one.

Turning the letters into 755

Each triad is a three bit number. r is 4, w is 2, x is 1. Add up the bits that are on.

  • rwx is 4 + 2 + 1 = 7
  • rw- is 4 + 2 = 6
  • r-x is 4 + 1 = 5
  • r-- is 4
  • --- is 0

drwxr-xr-x is therefore 7 for the owner, 5 for the group and 5 for other: 755. -rw-r--r-- is 6, 4, 4: 644. drwxrwxr-x is 7, 7, 5: 775, which is 755 with write added for the group. You never have to count characters by hand, because stat prints both forms at once.

stat -c '%A %a %U %G %n' ~/permdemo ~/permdemo/inner/notes.txt

%A is the letter form, %a is the octal, and %U and %G name the owning user and the owning group.

The permission strings people search for

These are the modes you meet on a real server, with the octal value and where each one turns up.

  • -rw-r--r-- is 644. Ordinary files a service only reads, such as a config file or an HTML page.
  • -rw------- is 600. Secrets: an SSH private key, or an application's .env file.
  • -rw-rw-r-- is 664. A file in a directory shared by a group, where teammates need write.
  • -rwxr-xr-x is 755. Scripts and binaries, such as /usr/local/bin/backup.sh and most of /usr/bin.
  • -rwx------ is 700. A private script that only its owner may run.
  • drwxr-xr-x is 755. Almost every system directory, and the document root of a website.
  • drwx------ is 700. ~/.ssh, and home directories on a locked down box.
  • drwxrwxr-x is 775. A directory the owner's group may write into.
  • drwxrwsr-x is 2775. The same, plus the setgid bit, so new files inside inherit the directory's group.
  • drwxrwxrwt is 1777. /tmp. The trailing t is the sticky bit, so a user may delete only their own files.
  • -rwsr-xr-x is 4755. A setuid binary that runs as its owner, such as /usr/bin/passwd and /usr/bin/sudo.
  • -rw-rw-rw- is 666, and drwxrwxrwx is 777. World writable, which on a server is nearly always a mistake.
  • lrwxrwxrwx is what every symbolic link shows. Linux ignores the mode bits on a link and checks the target instead, so this string carries no meaning at all.

Which triad applies to you

Here is the second rule beginners get wrong. The kernel picks exactly one triad and stops there.

If your user ID matches the file's owner, you get the owner triad, and the group and other bits are never consulted. Otherwise, if the file's group is one of your groups, you get the group triad. Otherwise you get the other triad.

Two things follow. The owner triad applies even when it is the most restrictive one. A file with mode 0466, printed as -r--rw-rw-, gives its owner read only while everybody else may write, because the owner check matched first and nothing after it was read. That is legal, and it confuses everyone the first time they meet it.

The group triad is then chosen by the file's group, not by the list of groups you belong to. ls -l prints two names on each line: the owner, then the group. Only that second group has any say over that file. Belonging to twenty groups helps only when the file carries one of them.

id
stat -c '%U %G %A %n' ~/permdemo/inner/notes.txt

id prints your user and every group you are in. stat prints the file's owner and group. Compare the two and you know which triad the kernel will use for you.

This is why a shared directory usually gets a group and the setgid bit. sudo chmod 2775 /srv/shared prints as drwxrwsr-x, and files created inside then inherit the directory's group instead of the creator's personal group, so the next person can still write them. Giving each service its own account is the other half of this story, and it is covered in one Linux user per service on a VPS.

umask decides the mode of every new file

A new file does not take its mode from you. It takes it from the program that created it, minus the bits your umask clears. The umask is a mask of bits to remove, so a larger umask produces more private files.

Most distributions ship 022. A program creating a regular file asks for 0666. A program creating a directory asks for 0777. The umask clears 022 from both requests, so you get 644 on files and 755 on directories. That is exactly the pair you see all over a fresh VPS.

umask
umask -S
touch ~/permdemo/new.txt && mkdir -p ~/permdemo/newdir
ls -ld ~/permdemo/new.txt ~/permdemo/newdir

umask -S prints the same value as letters, which is easier to read than 0022. Set umask 027 in ~/.profile for a stricter default: 640 on files and 750 on directories, so your group can read your work and nobody else can.

Two limits matter. The umask can only clear bits, never add them, which is why a newly created file is never executable whatever you set. And a systemd service never reads your shell profile, so set the value in the unit file instead.

[Service]
UMask=0027

Why web files are 644 and web directories 755

A web server runs under its own account, www-data on Debian and Ubuntu, nginx on Rocky and Alma. That process needs read on the files it serves and traverse on the directories above them. It has no reason to write them, and a static site should never allow it.

644 on a file gives write to the owner and read to everyone, so the deploy user can publish and the web user can serve. 755 on a directory gives write to the owner and traverse to everyone, so the web user can walk the path without being able to add or delete anything. A bug in the application then cannot rewrite the pages it is serving.

The traverse rule bites right here. If the site lives in /home/deploy/site and /home/deploy is 750, the web user cannot enter the home directory at all, and the request ends as HTTP 403 with a line like this in /var/log/nginx/error.log:

open() "/home/deploy/site/index.html" failed (13: Permission denied), client: 203.0.113.5

That 13 is EACCES, the kernel's permission refusal. Nothing is wrong with the network: the port is listening and the request arrived, which is what makes this one confusing while you are still learning how listening ports work on Linux. Run namei -l /home/deploy/site/index.html and read down the chain for the first directory without x for other.

A directory the application writes into, such as an upload path, is the exception. Grant that with ownership rather than with a wider mode: sudo chown -R www-data:www-data /srv/site/uploads and leave the mode at 755. Keep write access to the one directory that needs it.

chmod, without flattening the whole tree

chmod takes both forms. Octal sets all nine bits at once: chmod 644 notes.txt. Symbolic changes only what you name and leaves the rest alone: chmod u+x deploy.sh adds execute for the owner, and chmod go-w notes.txt removes write from group and other.

Recursion is where trees get damaged. chmod -R 755 . marks every image and every config file executable, because chmod cannot tell a script from a JPEG. Use the capital X instead.

chmod -R u=rwX,go=rX ~/permdemo
stat -c '%a %n' ~/permdemo ~/permdemo/inner/notes.txt

Capital X applies execute to directories, and to files that already had an execute bit somewhere. Directories come out 755, ordinary files come out 644, and scripts that were already executable stay that way. When one file already has a mode you trust, chmod --reference=good.sh other.sh copies it across.

The messages you see when a bit is wrong

bash: ./deploy.sh: Permission denied means the script has no x bit in the triad that applies to you, or a directory on its path has no x. chmod u+x deploy.sh fixes the first case.

bash: ./deploy.sh: cannot execute: required file not found is a different fault with a confusing name. The x bit is fine, and the interpreter named on the first line is missing. The usual cause is Windows line endings, so the kernel is looking for an interpreter called /bin/bash\r. Repair it with sed -i 's/\r$//' deploy.sh.

Permissions 0644 for '/home/deploy/.ssh/id_ed25519' are too open. comes from the SSH client, which refuses to use a private key that other accounts can read. The key wants 600 and ~/.ssh wants 700. Key handling in full is in managing SSH keys and their file permissions.

Authentication refused: bad ownership or modes for directory /home/deploy/.ssh appears in the server's journal when a home directory or its .ssh is group writable. The sshd StrictModes setting rejects the key, and from the client side that looks like an unexpected password prompt with no explanation.

sudo: /etc/sudoers is world writable, followed by sudo: no valid sudoers sources found, quitting, means sudo checked the mode of its own config and refused to run. That file must be 0440. This is the classic aftermath of a broad recursive chmod, and it can arrive together with the sshd message above, which leaves the provider's console as the only way back in.

Why 777 is not a fix

777 gives write access to every account on the machine, and to every process running under any of those accounts. A server runs services under their own users, so "everyone" covers far more on a VPS than it does on a laptop. A service that gets compromised can write wherever 777 allows.

Under a web root the harm is direct. A world writable directory that the server also serves means a file upload flaw becomes a way to plant a script and then request it back.

777 is almost always the wrong answer to an ownership question. The symptom is "the app cannot write to this directory". The cause is that the directory belongs to the wrong user. sudo chown -R appuser:appuser /srv/app/storage with mode 755 solves it and keeps every other account out. Creating those accounts before anything is deployed belongs in the first ten minutes on a new VPS.

World write is legitimate in one familiar place, /tmp, and it is spelled drwxrwxrwt. The trailing t is the sticky bit: the directory is writable by all, yet a user may delete only files they own. Without that bit, any account could delete another account's temporary files.

Read a mode before you change one

These commands only read state, so they are safe to run anywhere.

id
umask
stat -c '%A %a %U %G %n' ~/permdemo ~/permdemo/inner/notes.txt
namei -l ~/permdemo/inner/notes.txt
find ~/permdemo -perm -0002

find <path> -perm -0002 lists everything under a path whose world write bit is set, which is the fastest way to audit a box after somebody has been repairing things with chmod 777.

To ask whether a particular service account can enter a directory, ask as that account. sudo -u www-data test -x /srv/site && echo yes || echo no prints yes when that user has traverse on the directory, and no when it does not. Asking as root proves nothing, because root skips the check and the answer is always yes.

FAQ

What does drwxr-xr-x mean in Linux?

It is a directory, shown by the leading d, with mode 755. The owner triad is rwx, so the owner has every permission on it. The group triad is r-x and the other triad is r-x, so everybody else can list the names inside and pass through the directory, while nobody but the owner can add or remove anything. Confirm any path with stat -c '%A %a %U %G %n' <path>, which prints the letter form and the octal form side by side.

Why are web files 644 and web directories 755?

The web server runs under a different account, www-data on Ubuntu. It needs read on the files it serves and traverse on every directory above them, and it has no reason to write either one. 644 gives the owner write and everyone else read. 755 gives the owner write and everyone else traverse. A directory the application genuinely must write into should be handed over with chown to that application's user, rather than by widening the mode for everybody.

Does the x bit mean I can execute a directory?

No. On a directory x means traverse, which is the right to resolve one name inside it while the kernel walks a path. cd needs it, and so does every open of a file underneath it. Each directory along the path needs x, so a file with mode 644 is still unreachable when a directory above it lacks x for you. namei -l /path/to/file prints the mode of every directory in the chain, which shows where the lookup stops.

Is chmod 777 ever the right fix?

Almost never on a server. It grants write to every account on the machine, including the accounts that services run under, so one compromised service can rewrite the file. When an application cannot write a directory, the real problem is usually ownership: sudo chown -R appuser:appuser /srv/app/storage with mode 755 gives the application what it needs and leaves everyone else out. The familiar exception is /tmp at 1777, and that only works because the sticky bit stops users deleting each other's files.

Why does ls print a dot or a plus after the permissions?

That eleventh character describes rules beyond the nine permission bits. A dot, as in drwxr-xr-x., means an SELinux security context is attached, which is normal on Fedora and Rocky. A plus, as in drwxr-xr-x+, means a POSIX ACL (access control list) is set, so some user or group holds rights that the three triads do not show. Run getfacl <path> to list those extra entries.

#permissions#ls#chmod#linux-basics#octal