A clear walkthrough of Linux file permissions. Read the funny rwx- letters, change them safely with chmod, fix "permission denied" errors with confidence.
By the end of this post you'll be able to read any ls -l output at a glance, change permissions safely with chmod, and debug "permission denied" errors without guessing. About 20 minutes. You'll need a Linux or macOS terminal — both behave the same for the purposes of this tutorial.
Every file in Linux has three classes of users it cares about:
staff or wheel)For each class, three permissions can be granted:
That's the whole model. Owner/group/other × read/write/execute = nine bits per file.
ls -l output#Run:
ls -l
You'll see lines like:
-rw-r--r-- 1 alice staff 1.2K Apr 28 10:15 notes.txt
drwxr-xr-x 3 alice staff 96B Apr 28 10:15 projects
-rwxr-xr-x 1 alice staff 2.4K Apr 28 10:15 deploy.sh
The first column is the permission string. Decode it left to right:
-rwxr-xr-x
^|||++|+++
| | | |
| | | other: r-x (read, no write, execute)
| | group: r-x (read, no write, execute)
| owner: rwx (read, write, execute)
file type: - (regular file; "d" = directory, "l" = symlink)
So -rwxr-xr-x means: regular file, owner can do everything, group and other can read and execute but not write.
Practice:
-rw-r--r-- → owner can read+write, group and other can only readdrwx------ → directory, only owner has access (private dir)-rwxrwxrwx → file, anyone can do anything (almost always wrong)Once you can read these in your sleep, the rest is easy.
That -rwxr-xr-x string can be written as a 3-digit number: 755. This is what people mean when they say "chmod 755."
The mapping:
r = 4
w = 2
x = 1
Add them up per class:
rwx = 4+2+1 = 7r-x = 4+0+1 = 5r-- = 4+0+0 = 4--- = 0+0+0 = 0So rwxr-xr-x = 7-5-5 = 755. The three digits are owner-group-other.
Common ones to memorize:
rwxr-xr-x — executable scripts, public dirs (default for many)rw-r--r-- — regular files (default for most)rw------- — private files (SSH keys, secrets)rwx------ — private dirsrwxrwxrwx — almost always wrong; means anyone can do anythingThat's 90% of permissions you'll see in practice.
Two ways: numeric or symbolic.
Numeric is what scripts use:
chmod 644 notes.txt # owner rw, group/other r
chmod 755 deploy.sh # owner rwx, group/other rx
chmod 600 ~/.ssh/id_rsa # private (SSH requires this for keys)
Symbolic is more readable for one-off changes:
chmod +x deploy.sh # add execute for everyone
chmod u+w notes.txt # add write for owner (u = user/owner)
chmod g-w notes.txt # remove write from group
chmod o-rwx secrets.txt # remove all permissions from "other"
chmod a+r public.txt # add read for all (a = all classes)
Try it:
echo "test" > demo.txt
ls -l demo.txt # default 644
chmod 600 demo.txt
ls -l demo.txt # now -rw-------
chmod a+x demo.txt
ls -l demo.txt # now -rwx--x--x (oddly mixed)
The ls -l output should change each time, reflecting your new permissions.
chmod changes permissions. chown changes the owner and/or group:
sudo chown bob notes.txt # change owner to bob
sudo chown bob:developers notes.txt # owner=bob, group=developers
sudo chown :developers notes.txt # change only the group
Most operations need sudo because changing ownership is a privileged operation.
For directories with many files, -R (recursive) does the whole tree:
sudo chown -R bob:developers /var/www/myapp
Use -R carefully — it changes everything inside the directory.
A common confusion: what does "execute" mean on a directory?
For a directory, x means "you're allowed to enter it and access files inside." Without x, even if you have r, you can list filenames but can't actually open any of them.
mkdir test_dir
echo "hello" > test_dir/file.txt
chmod 644 test_dir # rw on dir, but no x
cat test_dir/file.txt # permission denied — no x means no enter
chmod 755 test_dir
cat test_dir/file.txt # works now
Practical rule: directories you want to access need x for whoever's accessing them. Standard public dirs are 755 (everyone can enter, only owner can write). Private dirs are 700.
When a command fails with permission denied, ask three questions in order:
whoami. The current user is the one whose permissions are being checked.ls -l <file>. If owner is someone else and you're not in the group, only "other" permissions apply.ls -l. Compare what you have against what you need.Example: trying to write to a file you don't own:
$ whoami
alice
$ ls -l /var/log/syslog
-rw-r----- 1 root adm 12k Apr 28 10:15 /var/log/syslog
Owner is root. Group is adm. Other has no permission. Alice is not root and (probably) not in adm — so she falls into "other" and has no read access. Solution: sudo cat /var/log/syslog, or sudo usermod -aG adm alice and re-login to add her to the group.
chmod 777 to fix problems. Setting 777 does work — anyone can do anything — but is almost never the right answer. It makes the file world-writable, which is a security disaster for anything sensitive. Find the right specific permissions instead.
Recursive chmod on the wrong directory. chmod -R 777 / (yes, people have done this) is the operating-system-equivalent of digging up your foundations. Always confirm the path before recursive chmod.
Confusing chmod and chown. chmod changes permissions, chown changes ownership. You'll mix them up the first 10 times — that's normal.
Thinking root bypasses permissions. Root doesn't get blocked by file permissions, but it still gets blocked by SELinux, AppArmor, and immutable bits (chattr +i). If sudo doesn't help, those are the next things to check.
Setting the wrong permissions on ~/.ssh. SSH refuses to use keys with too-permissive permissions. ~/.ssh should be 700, files inside should be 600 or 644. SSH will warn you loudly when this is wrong.
You've got the model. The next levels:
Linux file permissions look intimidating until they click. Once they do, "permission denied" stops being a mystery and starts being a 30-second debugging task. Every Linux user goes through this transition; the tutorial just makes it faster.
Get the latest tutorials, guides, and insights on AI, DevOps, Cloud, and Infrastructure delivered directly to your inbox.
Run your first three Kubernetes objects — Pod, Deployment, Service — on a local cluster, then understand why each one exists and how they fit together.
A hands-on intro to prompt engineering. Learn the four levers (role, format, examples, constraints) and watch a vague prompt turn into a reliable one.
Explore more articles in this category
Generate an SSH key, set up passwordless login, and configure aliases for the servers you use daily — all without copy-pasting yet another long command.
Build a real disk-cleanup script step by step. Learn variables, conditionals, loops, error handling, and the safety preamble that prevents foot-guns.
We started using eBPF tooling for ad-hoc production debugging six months ago. Three real incidents where it cut investigation time from hours to minutes.