Every Linux user meets the same wall sooner or later: a file that refuses to open, a script that refuses to run, or a blunt Permission denied in the middle of an otherwise fine afternoon. The fix is almost never to reach for sudo and hope. It is to read the nine characters at the start of an ls -l line, understand what they allow, and change exactly the bit that is wrong.
What you need
Any Linux system with a terminal, a scratch directory to practise in, and a normal user account. Run the examples in a throwaway folder, not in your home directory or anywhere system-owned:
mkdir -p ~/permdemo/shared
cd ~/permdemo
echo "hello" > notes.txt
printf '#!/bin/sh\necho hi\n' > deploy.shStep 1: read the permission string
ls -l prints one line per file. The first ten characters are the whole story:
$ ls -l
-rw-r--r-- 1 user user 6 Nov 8 13:53 notes.txt
drwxr-xr-x 2 user user 40 Nov 8 13:53 sharedThe first character is the file type: - for a regular file, d for a directory, l for a symlink. The next nine characters are three groups of three, always in the same order: owner, group, others. Each group has the same three slots: r (read), w (write), x (execute), with a - where a bit is off.
So -rw-r--r-- is a regular file the owner can read and write while everyone else can only read. drwxr-xr-x is a directory the owner fully controls while others can look inside but not change anything. stat prints the same facts without the noise:
$ stat -c '%A %a %U %G %n' notes.txt shared
-rw-r--r-- 644 user user notes.txt
drwxr-xr-x 755 user user sharedStep 2: turn the letters into numbers
Each permission has a value: read is 4, write is 2, execute is 1. Add them per group and you get one digit per group, the octal mode you will see everywhere:
| Letters | Number | Meaning |
| --- | --- | --- |
| rwx | 7 | read, write, execute |
| rw- | 6 | read and write |
| r-x | 5 | read and execute |
| r-- | 4 | read only |
| --- | 0 | nothing |
That gives you the modes you will meet on a real system. 644 is the standard for ordinary files: you can edit them, everyone else can read them. 755 is the standard for directories and for scripts and binaries. 600 is for secrets only the owner may read, and 700 is for private directories. Learn those four and you can read most of a system at a glance; the same ls -l output appears in How to Master the Linux Command Line Basics in a Weekend.
Step 3: change permissions with chmod
chmod changes the mode, and it speaks two dialects. Octal sets all nine bits at once:
chmod 644 notes.txt # owner read/write, everyone else read
chmod 755 deploy.sh # owner full control, everyone else read/execute
chmod 600 ~/.ssh/id_ed25519 # private key: owner only, no exceptionsSymbolic mode changes only the bits you name and leaves the rest alone, which makes it the safer everyday choice:
chmod u+x deploy.sh # add execute for the owner only
chmod g-w notes.txt # remove write for the group
chmod o=r notes.txt # set others to read only, exactly
chmod a+r notes.txt # add read for everyoneThe letters are u (owner), g (group), o (others), a (all), and the operators are +, -, and =. A common beginner moment is writing a script, running it, and getting Permission denied even though the script is right there. The file simply lacks the execute bit; chmod u+x script.sh is the whole fix, no sudo required, because you own the file.
Step 4: change ownership with chown
Permissions answer "what can each group do", but someone has to decide which user and group a file belongs to. That is chown:
sudo chown alice:developers notes.txt # new owner alice, new group developers
sudo chgrp developers notes.txt # group onlyChanging the owner needs root, which is why sudo shows up here and not in the previous step. Two warnings from real breakage: never run a recursive chown on a system directory (sudo chown -R user /usr will ruin the system in seconds), and always check the target first with ls -ld before adding -R. Ownership mistakes can take a reinstall to undo; permission mistakes usually take one chmod to undo.
Step 5: understand umask, or why new files come out 644
New files do not appear with random permissions. The kernel starts files at 666 and directories at 777, then subtracts your umask, so with the common desktop umask of 022 a new file lands at 644 and a new directory at 755. Check yours with the bare command:
$ umask
0022If you want new files private by default, a umask of 077 does it: new files come out 600, new directories 700. Set it in your shell profile and remember it only affects files created afterwards.
Step 6: directories play by different rules
The same three letters mean something slightly different on a directory. Read lists its names, write creates, deletes, and renames entries inside it, and execute lets you pass through it to reach things inside. That last one surprises people: a file can be world-readable and still unreachable if one directory above it lacks the execute bit for you. When a path fails with Permission denied and the file itself looks fine, walk the path upwards with namei -l, which prints every component with its mode so the blocking directory jumps out. The same check explains many web server 403 errors.
Two special bits finish the picture. The setgid bit on a directory (chmod 2775 /srv/shared, shown as drwxrwsr-x) makes new files inside inherit the directory's group instead of the creator's personal group, which is how a team shares a folder without constant chgrp. The sticky bit (chmod +t /srv/shared, shown with a t at the end) lets anyone create files in a directory, but only the file's owner can delete them; /tmp uses it, and so should any shared upload folder.
The traps to avoid
chmod 777 is never the fix. It makes a file readable, writable, and executable by every account on the system, including compromised services. If a web app cannot read a file, fix the owner or the group, not the whole world.
Recursive chmod flattens the tree. chmod -R 755 . marks every image and config file executable, because chmod cannot tell a script from a JPEG. When a whole tree needs fixing, separate files from directories with find, or use capital X: chmod -R a+rX directory adds execute only to directories and to files that are already executable.
SSH keys are the strictest case. OpenSSH refuses a private key that other users can read. The fix is always chmod 600 ~/.ssh/id_ed25519 and chmod 700 ~/.ssh, never looser; see How to Set Up SSH Keys on Linux for the full flow.
Testing as root hides every permission problem. Root bypasses the permission bits entirely, so a script that works under sudo and fails as your user is just owned by the wrong account. Always test as the user who will actually run the thing.
Verify what you changed
After any fix, prove it instead of assuming it. Re-read the mode, then actually try the operation that failed:
stat -c '%A %a %U %G %n' deploy.sh
./deploy.sh # should print: hiIf the original error came from a service rather than your shell, check its logs as the final confirmation. A Permission denied line next to the exact filename tells you which file and which bit are still wrong; How to Read Linux Logs with journalctl shows how to pull that line out of the noise.
That is the whole model: three groups, three bits, one number per group. Read the string, change the one bit that is wrong, and leave the rest alone.
Sources
- GNU coreutils manual,
chmodinvocation andchowninvocation - Arch Wiki, File permissions and attributes
chmod(1),chown(1),umask(1), andnamei(1)manual pages, as shipped on Debian and Ubuntu


