Linux File Permissions and chmod Explained with Examples
You deploy a script, it fails with "Permission denied," and your first instinct is chmod 777 because it makes the error go away. It does — and now any user on that machine can read, write, and execute the file. On a shared server or a container with a compromised process, that's a real hole, not a shortcut. Understanding what the permission bits actually mean takes ten minutes and removes the temptation entirely.
Reading the permission string
ls -l deploy.sh
# -rwxr-xr-- 1 nitheesh staff 812 Sep 12 10:03 deploy.sh
Break -rwxr-xr-- into four groups:
-— file type (-= regular file,d= directory,l= symlink)rwx— owner's permissions (read, write, execute)r-x— group's permissions (read, execute — no write)r--— everyone else's permissions (read only)
So nitheesh can edit and run this script, anyone in the staff group can run it but not edit it, and everyone else can only view it.
chmod with symbolic notation
chmod u+x deploy.sh # add execute for the owner
chmod g-w deploy.sh # remove write for the group
chmod o=r deploy.sh # set "others" to read-only, exactly
chmod a+r deploy.sh # add read for everyone (a = all)
This is usually clearer than numeric mode when you're making a targeted change, since it reads like what it does.
chmod with numeric (octal) notation
Each permission is a bit: read = 4, write = 2, execute = 1. Add them per group:
chmod 755 deploy.sh
# owner: 7 = rwx (4+2+1)
# group: 5 = r-x (4+0+1)
# other: 5 = r-x (4+0+1)
chmod 644 config.json
# owner: 6 = rw- (4+2+0)
# group: 4 = r-- (4+0+0)
# other: 4 = r-- (4+0+0)
755 is the standard mode for executable scripts and directories. 644 is standard for regular files that shouldn't be executable — config files, source code, data files.
Why 777 is almost always wrong
chmod 777 deploy.sh
# rwxrwxrwx — literally everyone can read, edit, and run this file
On a single-user dev machine this is mostly harmless. On a shared server, in a Docker image, or on anything internet-facing, it means any process running as any other user — including a compromised web app on the same box — can modify the file. The "permission denied" error you were fixing is almost always solved by chmod +x (adding execute for the owner) or fixing actual ownership with chown, not by opening everything to everyone.
Ownership matters as much as the bits
chown nitheesh:staff deploy.sh # set owner and group
sudo chown www-data:www-data /var/www/app # common web server pattern
Permission bits are meaningless without knowing who they apply to. A file owned by root with 644 permissions can't be edited by your deploy user even though 644 "looks" reasonable — because your user falls into the "other" bucket, which only has read access.
Directories need execute to be enterable
This confuses people coming from other OSes: on a directory, the execute bit doesn't mean "run it," it means "you can cd into it and access files inside by name."
chmod 755 mydir # can list, enter, and traverse
chmod 644 mydir # can list contents, but NOT cd into it — usually not what you want
Common mistakes
- Reaching for
777(orchmod -R 777) as a generic fix for permission errors, instead of diagnosing what's actually wrong — usually wrong ownership or a missing execute bit. - Running
chmod -Rrecursively without realizing it applies the same mode to both files and directories, which often breaks directory traversal (files don't need execute; directories do). - Confusing
chmod(permission bits) withchown(ownership) — "permission denied" is often actually an ownership problem, not a mode problem. - Setting SUID/SGID bits (
chmod 4755) without understanding they let a script run with the file owner's privileges regardless of who executes it — a real privilege escalation risk if misused.
What I'd actually use
755 for executables and directories, 644 for everything else, and fix ownership with chown before ever reaching for broader permissions. If something genuinely needs group-shared write access, use a shared group and chmod 664/775 rather than opening it to the entire system with 777.
Next steps
Run find /path/to/project -perm -o+w to find any world-writable files in your project — that's a quick audit for accidental 777s left over from past "just make it work" fixes.