Linux File Permissions Explained: chmod, chown and the 777 Trap

Permission denied, nginx 403, SSH key rejected, the 777 habit. This guide teaches Linux file permissions properly: the nine bits, octal and symbolic chmod, ownership and groups, the special bits, and the exact commands that fix the five permission problems students hit most — without ever reaching for 777.

Written by Projectech10 min readPublished
For B.E./B.Tech students deploying projects on a Linux VPS or editing files over SSH who keep hitting Permission denied and fix it by randomly chmod-ing things Topics: Linux, Bash, VPS, SSH
Blueprint-style illustration of a file-folder tree diagram with a padlock and three branching permission paths, showing owner, group and other access levels.
Illustration generated for this guide.
In this guide

Your deployment script fails with "Permission denied." A tutorial says to run chmod -R 777 and it works — so you ship it. Months later your project directory is world-writable, your SSH key permissions are broken, and nginx throws a 403 you cannot explain. Every one of these has the same root cause: you never learned what the nine permission characters in ls -l actually mean.

This guide explains Linux file permissions the way they should have been taught: the three classes, the nine bits, symbolic vs octal notation, ownership, the special bits, and the exact commands that fix the five permission problems students hit most — without ever reaching for 777.

Reading the nine characters

Run ls -l on any Linux system and you see something like this:

-rw-r--r-- 1 ved students  2048 Oct 10 09:12 app.py

Ignore the leading - (file type: - for file, d for directory, l for link). The next nine characters are three groups of three:

  • Characters 2-4: what the owner (user ved) can do: rw- = read + write, no execute.
  • Characters 5-7: what the group (students) can do: r-- = read only.
  • Characters 8-10: what everyone else can do: r-- = read only.

Each position is one permission:

  • r (read): read the file's contents. For a directory: list the filenames inside it.
  • w (write): modify the file. For a directory: create, delete, or rename files inside it — this is the subtle one, covered below.
  • x (execute): run the file as a program. For a directory: enter it and access files inside by name.

The directory case trips up everyone: to open /var/www/site/index.html you need execute permission on every directory in the path (/, /var, /var/www, /var/www/site), even if the file itself is world-readable. nginx 403 errors are usually a missing +x on some parent directory, not a problem with the file.

And write-on-a-directory is its own privilege: deleting a file requires write permission on the directory, not on the file. A read-only file in a writable directory can still be deleted by the directory's owner.

Octal notation: the three numbers

Each group of three bits is one octal digit (r=4, w=2, x=1; add them):

Symbol Octal Meaning
rwx 7 Full control
rw- 6 Read + write
r-x 5 Read + execute
r-- 4 Read only
--- 0 Nothing

So 644 = rw-r--r-- (owner reads/writes, everyone else reads) and 755 = rwxr-xr-x (owner full, everyone else read + execute). The two defaults you should memorise:

  • Files: 644 — you edit them; the world reads them.
  • Directories: 755 — you control them; the world can enter and list.

When you create files, the system applies a umask that strips bits from the maximum (666 for files, 777 for directories). The common umask 022 strips write for group and others: 666 - 022 = 644, 777 - 022 = 755. That is where the defaults come from.

Symbolic notation: changing one thing

Octal sets all nine bits at once; symbolic mode changes one class's one permission without touching the rest:

chmod u+x deploy.sh        # give the owner execute permission
chmod g-w secrets.txt      # take write away from the group
chmod o= app.key           # others get nothing at all
chmod a+r README.md        # everyone can read (a = all)

Use symbolic notation when you know exactly which bit is wrong; use octal when you are setting a known-good standard (644, 755, 600).

Ownership: chown and chgrp

Every file has an owner and a group. Two commands change them:

chown ved app.py                 # change the owner
chown ved:students app.py        # change owner and group together
chgrp students shared/           # change just the group

Only root can give away ownership (changing chown as a normal user is restricted for security reasons — otherwise you could create files owned by someone else and bypass quotas). Use sudo chown when fixing ownership of deployed files.

The group exists so a set of users can share access: put your project collaborators and the web server user in one group, set the directory's group accordingly, and you never need to open permissions to the world.

The special bits: setuid, setgid, sticky

Three extra bits appear in the execute position as s, S, t, or T:

  • setuid (4): the file runs with the owner's privileges, not the runner's. This is how passwd lets you change your own password (it needs root to edit the password database). A security red flag on anything you did not install yourself.
  • setgid (2): on a file, runs with the group's privileges. On a directory, new files inside inherit the directory's group instead of the creator's primary group — the useful one for shared project folders.
  • sticky bit (1): on a directory (classically /tmp), anyone can create files but only the file's owner can delete them. That is why /tmp shows drwxrwxrwt.
chmod 4755 script.sh    # setuid + 755
chmod 2755 shared/      # setgid on directory: new files inherit the group
chmod 1755 uploads/     # sticky: users can't delete each other's files

In octal these sit in a fourth leading digit (4/2/1). You will rarely set them yourself; you must be able to read them when auditing a system.

The 777 trap: why it "works" and why it is wrong

chmod -R 777 makes everything readable, writable, and executable by everyone. It fixes every permission error instantly — and creates three real problems:

  1. Security: any process, any other user on the machine, any compromised script can now modify your code, read your secrets, or plant executables. On a shared VPS or lab machine this is not theoretical.
  2. SSH breaks: OpenSSH refuses to use keys that are group- or world-writable. chmod 777 ~/.ssh/id_rsa does not make SSH more permissive — it makes SSH stop working entirely, with an unhelpful "bad permissions" error.
  3. Executables everywhere: every file, including your data files and logs, becomes "executable," which confuses tools and scanners and masks real problems.

The right fix for a permission error is always the same three questions: which file, which user, which bit? Then change exactly that bit.

The five permission problems students actually hit

1. "Permission denied" running a script

./deploy.sh        # Permission denied
chmod u+x deploy.sh
./deploy.sh        # works

Files created by editors and downloads are not executable by default. Granting execute to the owner is the whole fix.

2. nginx / Apache 403 Forbidden

The web server runs as its own user (often www-data) and needs read on the file plus execute on every parent directory:

sudo chown -R ved:www-data /var/www/site
sudo chmod -R 755 /var/www/site
sudo chmod -R 644 /var/www/site/*.html

If 403 persists after this, check a parent directory (namei -l /var/www/site/index.html shows the permission of every path component — the fastest way to find the culprit).

3. SSH "bad permissions" / key ignored

SSH demands strict private-key permissions:

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

This is the number-one fix for "Permission denied (publickey)" on a correctly configured server. The SSH keys guide covers the key side of this problem.

4. Cannot write to an uploaded/shared folder

For a folder two users (you and the web server) must both write to, use a shared group plus setgid:

sudo chgrp -R webteam uploads/
sudo chmod -R 2775 uploads/   # group-writable, new files inherit the group

5. Git shows every file as modified after a Windows/Linux switch

Git tracks the executable bit. Files checked out on Windows gain +x and every file shows as changed. Fix per-repo:

git config core.fileMode false

A 60-second permission audit

When something is broken and you do not know where, run this sequence:

  1. ls -l <file> — read the nine characters. Who owns it? Which class is your user in (owner, group, other)?
  2. id and groups — confirm which user and groups you actually are right now (sudo changes this).
  3. namei -l <path> — check every directory in the path for a missing x.
  4. ps aux | grep nginx (or your service) — confirm which user the failing process runs as; fix permissions for that user, not for yourself.
  5. Change the minimum: one bit, one class. Re-test before changing anything else.

Common mistakes

  • Recursive chmod on the wrong directory. chmod -R 644 /var/www strips execute from every directory, making them all un-enterable. If you must recurse, do directories and files separately: find . -type d -exec chmod 755 {} + and find . -type f -exec chmod 644 {} +.
  • Running everything as root via sudo instead of fixing ownership — it works, then one typo deletes the wrong tree with full privileges.
  • World-writable config files holding API keys or database passwords (-rw-rw-rw- on .env is a leak waiting to happen; use 600).
  • Confusing read on a directory with access. r-- on a directory lets you list names but not open anything inside without x.
  • Forgetting the group exists. Jumping to "others" permissions (or 777) when a shared group plus 770/775 was the correct, tighter answer.
  • Ignoring umask on new files. Uploads and scripts keep arriving with wrong permissions because the creating process's umask was never set right — fix the umask, not each file.

Frequently asked questions

What is the difference between 644 and 755?
644 (rw-r--r--) is for files: owner can edit, everyone reads. 755 (rwxr-xr-x) is for directories and scripts: adds the execute bit so others can enter directories or run the script. Never 755 a data file, never 644 a directory you need to enter.

Why does SSH care about my key file permissions?
A private key readable by others is a compromised key. OpenSSH checks this and refuses to use a key that is group- or world-accessible (it must be 600). It is a safety rail, not a bug.

What does the t in drwxrwxrwt mean?
The sticky bit on /tmp: anyone can create files there, but only the file's owner (or root) can delete or rename them. Without it, any user could delete anyone else's temporary files.

How do I see permissions of every folder in a path at once?
namei -l /full/path/to/file prints each path component with its permissions in one view — the fastest way to find which parent directory is blocking access.

Is sudo chmod -R 777 /project ever acceptable?
On a disposable local container you will throw away in an hour: harmless. Anywhere else — a VPS, a lab machine, anything with real data or a network port: no. Learn the three questions (which file, which user, which bit) instead.

Limitations

  • This guide covers classic Unix discretionary permissions. Mandatory access control (SELinux, AppArmor) sits above these and can deny access even with perfect 755/644 — if permissions look right but access still fails on a hardened distro, check SELinux contexts (ls -Z).
  • Access Control Lists (setfacl/getfacl) allow finer per-user rules than the nine bits; they are out of scope here but worth learning next.
  • Permission models differ on network filesystems (NFS, SMB) and on Windows/WSL mounts — bits may be emulated or ignored there.

Where to go from here

More project guides

More in Computer / IT