datarekha
CLI June 7, 2026

Unix file permissions: rwx, chmod, and the octal that confuses everyone

Understand Unix file permissions end-to-end: read the ls -l string, master chmod octal and symbolic modes, chown, umask, and special bits.

11 min read · by datarekha · command linefile permissionschmodchownlinux

You have almost certainly seen this:

$ ssh git@github.com
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/you/.ssh/id_rsa' are too open.

The fix is one command — chmod 600 ~/.ssh/id_rsa — but understanding why 600 is correct, and why 644 is not, requires a working mental model of Unix file permissions. That model also explains every “Permission denied” error you will ever hit, lets you reason about shared-server security, and makes ls -l output readable at a glance instead of a wall of symbols.

This post builds that model from the ground up. See also the CLI overview and the files and directories reference for related command coverage.

Reading ls -l: the ten-character string

Run ls -l in any directory and the leftmost column looks like this:

-rwxr-xr-x
drwxr-sr-x
-rw-r--r--

The string is always ten characters. The first character is the file type:

  • - regular file
  • d directory
  • l symbolic link
  • c character device
  • b block device

The remaining nine characters are three rwx triplets, one each for:

  1. The user (owner) of the file
  2. The group assigned to the file
  3. Other — everyone else on the system

Each triplet occupies exactly three positions: r, w, x, or - if that permission is absent.

-rwxr-xr-xtypefileuser (owner)rwx = 7groupr-x = 5otherr-x = 5octal: 0755
The ten-character ls -l string: file type followed by three rwx triplets. Each triplet converts to a single octal digit.

What r, w, x actually mean — and the directory gotcha

For a regular file the semantics are straightforward:

  • r (read) — open the file and read its contents
  • w (write) — modify or truncate the file
  • x (execute) — run the file as a program or script

For a directory the meanings shift, and this is where most beginners get confused:

  • r — list the names of entries in the directory (ls works)
  • w — create, rename, or delete entries inside the directory
  • x — enter or traverse the directory (cd works; path lookup through it works)

The subtle consequence: you can have x without r on a directory. If a directory is --x for you, you can cd into it and open files whose names you already know, but ls will return “Permission denied.” Conversely, r without x lets you see file names but not open them or cd in. In practice, directories are almost always given both or neither, but the distinction matters when you are debugging a production server path where a deploy script can traverse /var/app but not list it.

The octal model: r=4, w=2, x=1

Each permission bit has a numeric value: r = 4, w = 2, x = 1. Add the values that are set in a triplet to get one digit, 0–7. Do that for user, group, and other and you have a three-digit octal number.

Bit values per positionr= 4w= 2x= 1Sum for each triplet → one octal digitCommon tripletsrwx4+2+1 =7r-x4+0+1 =5rw-4+2+0 =6r—4+0+0 =4Full examples755→ rwx r-x r-x (scripts, executables, directories)644→ rw- r— r— (regular files, config, source code)
Each triplet sums to one octal digit. 755 and 644 are the two most common permission modes you will encounter.

The common modes you will use every day:

  • 755 (rwxr-xr-x) — owner can do everything; everyone else can read and enter. Standard for directories and publicly accessible scripts.
  • 644 (rw-r--r--) — owner can read and write; everyone else can only read. Standard for text files, configs, source code.
  • 600 (rw-------) — owner can read and write; everyone else has no access. Use for SSH private keys, credentials files, secrets.
  • 700 (rwx------) — owner can do everything; no one else has any access. Useful for private scripts or directories containing sensitive data.

chmod: numeric and symbolic modes

chmod (change mode) accepts either numeric octal or symbolic notation.

Numeric mode sets permissions absolutely. Whatever you write replaces the current mode:

chmod 755 myscript.sh     # rwxr-xr-x
chmod 644 config.yaml     # rw-r--r--
chmod 600 ~/.ssh/id_rsa   # rw-------
chmod 700 private-dir/    # rwx------

Symbolic mode lets you add (+), remove (-), or set exactly (=) permissions for specific principals: u (user/owner), g (group), o (other), or a (all three):

chmod u+x deploy.sh       # add execute for owner only
chmod go-w shared.txt     # remove write from group and other
chmod a=r public.txt      # set read-only for everyone
chmod o-rx secret/        # remove read and execute from other
chmod ug=rw,o= data.csv   # owner+group get rw, other gets nothing

Symbolic mode is useful in scripts where you want to modify one bit without overwriting the rest. Numeric mode is clearer for absolute assignments and is what you will see in documentation, dockerfiles, and sysadmin playbooks.

To apply a mode recursively to a directory and everything inside it, add -R:

chmod -R 755 /var/www/html

Use -R carefully: applying 755 recursively to a directory that contains regular files would make those files executable, which is rarely what you want. Many sysadmins use find to apply different modes to files and directories separately:

find /var/www/html -type d -exec chmod 755 {} +
find /var/www/html -type f -exec chmod 644 {} +

chown and chgrp: changing ownership

Permissions only matter in the context of who owns the file. chown changes the owning user, optionally the owning group at the same time:

chown alice report.txt              # change owner to alice
chown alice:editors report.txt      # change owner and group
chown -R deploy:www /var/app        # recursive

chgrp changes only the group:

chgrp editors report.txt

Changing ownership of a file you do not own requires sudo. Regular users can only change the group of a file they own to a group they themselves belong to.

umask: why new files default to 644 or 755

When a program creates a new file, the kernel starts from a maximum permission set — typically 666 for files (no execute by default) and 777 for directories — and then subtracts your umask. The umask is a bit mask of permissions to remove, not grant.

The default umask on most Linux systems is 022:

666 - 022 = 644   (new files)
777 - 022 = 755   (new directories)

Check your current umask:

umask        # shows e.g. 0022
umask -S     # symbolic form: u=rwx,g=rx,o=rx

Set a stricter umask for a session where you are creating sensitive files:

umask 077    # new files = 600, new dirs = 700

The umask is a per-process value inherited from the shell. It is not a permission check on existing files — it only affects creation.

When you need sudo

sudo runs a command as root. You need it to:

  • Change ownership of files you do not own (chown on other users’ files)
  • Write to system directories (/etc, /usr, /var/log, etc.)
  • Change permissions on files owned by other users
  • Read files with mode 600 owned by another user

A common mistake is running chmod without sudo on a system file, getting “Operation not permitted,” and then running chmod on the wrong file because the path was wrong. Always confirm the path with ls -l before reaching for sudo.

Special bits: setuid, setgid, sticky

Beyond rwx, Unix has three special permission bits. They appear as a leading octal digit.

Setuid (4xxx) on an executable file causes it to run as the file’s owner, not the invoking user. The classic example is /usr/bin/passwd, which is owned by root but must write to /etc/shadow. It shows as rws (or rwS if execute is not also set) in the user position:

ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root ...

Setuid on directories is ignored on Linux (it has meaning on some BSDs, but not the same meaning).

Setgid (2xxx) on an executable runs it with the file’s group identity. More practically, setgid on a directory causes new files created inside to inherit the directory’s group rather than the creator’s primary group. This is invaluable for shared project directories:

chmod g+s /shared/project    # new files inherit 'project' group

Sticky bit (1xxx) on a directory means only the file’s owner (or root) can delete or rename that file, even if others have write permission on the directory. The canonical example is /tmp, which is world-writable but sticky so users cannot delete each other’s temporary files:

ls -ld /tmp
drwxrwxrwt 20 root root ...    # 't' = sticky + execute

Set special bits by adding the leading digit:

chmod 2775 /shared/project   # setgid directory
chmod 1777 /tmp              # sticky world-writable directory
chmod 4755 /usr/local/bin/mytool  # setuid executable (use carefully)

Practical scenarios

Make a shell script executable:

chmod u+x deploy.sh
# or absolutely:
chmod 755 deploy.sh

Fix “Permission denied” on a web server: the web server process runs as a user like www-data. If your files are owned by you and mode 600, the server cannot read them:

chown -R you:www-data /var/www/mysite
chmod -R 750 /var/www/mysite
find /var/www/mysite -type f -exec chmod 640 {} +

Lock down an SSH private key:

chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
chmod 700 ~/.ssh

Protect a secrets file:

chmod 600 .env

Check who owns a file before blaming permissions:

ls -la ~/.ssh/

The security mindset: least privilege

The principle of least privilege means a file, process, or user should have exactly the permissions required to do its job and no more. In practice this means:

  • Prefer 640 over 644 for files that only the owner and a trusted group need to read. Check the glossary entry for “principle of least privilege” for a broader definition.
  • Prefer 750 over 755 for directories whose contents are not public.
  • Use 600 for any file containing a secret: API keys, private keys, password files, .env files.
  • Audit setuid binaries periodically with find / -perm -4000 -type f 2>/dev/null. Every setuid binary is a privilege-escalation vector if it has a vulnerability.
  • Remember that root ignores read and write permission bits (though not execute on regular files). sudo is a trust boundary, not a permissions fix.

Frequently asked questions

Why does chmod +x sometimes produce rwxr-xr-x and sometimes rwx--x--x?

When you use a+x or bare +x, the kernel applies the execute bit to all three triplets but then masks it through your umask. With a umask of 022, group-execute and other-execute are preserved only if they were already set or if the umask allows them. For a clean absolute assignment, use numeric mode: chmod 755 file.

What happens if a directory has w but not x?

Essentially nothing useful. Write permission on a directory means “create or delete entries,” but you cannot do either without also being able to enter the directory (x). A directory that is rw- for you is functionally the same as r-- — you can list names but cannot act on them or traverse into it.

Can I read a file inside a directory I cannot list?

Yes, if you already know the full path and have read permission on the file itself. Directory x (traverse) is what path resolution requires; directory r (list) is only needed to discover names. This is occasionally used intentionally: a directory with mode 711 lets users access known paths but prevents enumeration of what else lives there.

Does chmod -R 755 . make all my files executable?

Yes, including regular text files, images, and data files — which is almost never what you want. The pattern to use instead is to apply 755 to directories and 644 to files in two separate find passes, as shown in the chmod section above.

Skip to content