Linux 📖 14 min read

Understanding Linux Permissions (Beyond chmod 777)

chmod 777 makes a "Permission denied" error go away by giving every account on the machine full control of the file. This guide explains what the read, write and execute bits mean on files and directories, how ownership and groups decide who gets which bits, what the special bits and ACLs add, and how to find the one permission that's actually missing.

Permission Octal What it means When to use
-rw------- 600 Owner reads and writes. Nobody else touches it. SSH keys, .env files, anything with passwords
-rw-r--r-- 644 Owner reads and writes. Everyone else reads only. HTML files, configs, most regular files
drwx------ 700 Owner has full access. Nobody else can even list contents. ~/.ssh/, private home directories
-rwxr-xr-x 755 Owner has full access. Everyone else can read and execute. Scripts, executables, web-accessible directories
-rwxr-x--- 750 Owner has full access. Group can read and execute. Others get nothing. Shared team directories, group-restricted scripts
-rwxrwxrwx 777 Everyone can do everything. No restrictions at all. Not on files you care about. Shared scratch directories such as /tmp use 1777, which adds the sticky bit.

What you need: a terminal on a Linux machine. The examples that change ownership or touch files outside your home directory use sudo.

Versions: the examples use the Linux command syntax documented in chmod(1), chown(1) and stat(1) from GNU coreutils, which Debian, Fedora, RHEL and Arch ship. Ubuntu 25.10 and 26.04 use the Rust reimplementation (uutils) for most of these commands by default; its release notes say it isn't fully compatible yet and that the GNU versions remain available with a gnu prefix, for example gnustat. The permission bits themselves work the same everywhere, including macOS and the BSDs, whose stat takes -f with its own format letters instead of -c (see FreeBSD stat(1)).

Read, Write, Execute

Every file and directory in Linux has three types of permissions:

  • Read (r) — You can see what's inside. For files, this means viewing the content. For directories, this means listing the files in it.
  • Write (w) — You can modify it. For files, this means editing. For directories, this means creating or deleting files inside it.
  • Execute (x) — You can run it. For files, this means running it as a program or script. For directories, this is a weird one: it means you can "enter" the directory with cd.

The directory execute permission trips people up. A directory with read but not execute lets you list filenames with ls, but you cannot cd into it or access any file inside it. To open /var/www/mysite/index.html, a process needs execute on /, /var, /var/www and /var/www/mysite, plus read on the file itself.

User, Group, Others

Those three permissions (rwx) are defined separately for three categories:

Captured terminal output: Reading ls -l on a new file
A new file starts as 644 (rw-r--r--) because the umask is 022. Captured on Ubuntu 26.04.1 under WSL 2.
  • Owner (u) — The user who owns the file. Usually whoever created it.
  • Group (g) — A group of users. Every file belongs to one group, and all members of that group share these permissions.
  • Others (o) — Everyone else on the system who isn't the owner and isn't in the group.

When you run ls -l, you see something like this:

Example ls -l line
-rw-r--r-- 1 alice developers 2048 Jan 9 10:30 notes.txt

That -rw-r--r-- means:

  • The first character (-) tells you it's a regular file. If it were a directory, you'd see d.
  • The next three characters (rw-) are the owner's permissions: read, write, no execute.
  • The next three (r--) are the group's permissions: read only.
  • The last three (r--) are everyone else's permissions: also read only.

In this example, alice (owner) can read and edit the file. Anyone in the "developers" group can read it but not change it. Everyone else on the system can also read it. Fine for a notes file. Not fine for anything containing a password.

Octal Notation

In chmod 755 script.sh, each digit represents the permissions for owner, group, and others — in that order.

Each permission has a value:

  • Read = 4
  • Write = 2
  • Execute = 1

You add them up to get the number. So:

  • 7 = 4 + 2 + 1 = read + write + execute (full permissions)
  • 6 = 4 + 2 = read + write
  • 5 = 4 + 1 = read + execute
  • 4 = just read
  • 0 = nothing

So when you see chmod 755, it means:

  • 7 for owner — full access (read, write, execute)
  • 5 for group — read and execute, but can't modify
  • 5 for others — same, read and execute only

This is perfect for a script you want everyone to be able to run, but only you should edit. And chmod 644 is standard for regular files: owner can read and write, everyone else can only read.

Now you understand why chmod 777 is almost always wrong. You're saying "let literally anyone read, modify, and execute this file." That's rarely what you want.

How chmod 777 Goes Wrong

How chmod 777 typically goes wrong:

A PHP app can't write to its upload directory, so someone runs chmod -R 777 /var/www/ to make the error go away. Now every file under the web root is readable and writable by every account on the machine. On a shared server, any other user (or any compromised process running as another user) can read config.php and the database password in it, or edit a PHP file and add their own code, which the web server will then run. The actual fix was much smaller: give the web server's user write access to the one upload directory (sudo chown www-data:www-data /var/www/mysite/uploads, mode 755 or 770), and keep config.php at 640, owned by the deploy user with the web server's group.

Changing Ownership with chown

Often the problem isn't the permission bits but ownership. Linux checks whether you are the file's owner, in its group, or neither, and applies only that set of bits, plus any ACL. A file owned by root can still be writable to you through its group or an ACL. Check the owner and group with ls -l and any ACL with getfacl before changing anything.

The chown command changes ownership:

Bash
# Change owner to alice
sudo chown alice myfile.txt

# Change owner AND group
sudo chown alice:developers myfile.txt

# Recursively change everything in a folder
sudo chown -R alice:developers /var/www/mysite/

Check it worked: stat -c '%U:%G %n' myfile.txt prints the new owner and group. Before a recursive chown on anything outside your home directory, record the current state so you can put it back: sudo find /var/www/mysite -printf '%u:%g %m %p\n' > ~/mysite-perms-before.txt.

This comes up constantly with web servers. Files uploaded via FTP are owned by the FTP user, but the web server runs as www-data. Wrong ownership means "permission denied" that no amount of chmod will fix. chown is the answer, not 777.

Sticky Bit and Special Permissions

You might notice that the /tmp directory has weird permissions:

Captured terminal output: The sticky bit on /tmp
/tmp is 1777. The trailing t is the sticky bit: users can delete only their own files. Captured on Ubuntu 26.04.1 under WSL 2.
Example ls -ld /tmp line
drwxrwxrwt 15 root root 4096 Jan 9 10:45 /tmp

To check the exact permissions on a directory, stat -c '%A %a %U:%G %n' /tmp prints the symbolic mode, the octal mode, owner, group and name.

See that t at the end instead of the usual x? That's the sticky bit. It means: even though everyone can write to this directory, you can only delete files you own. Without it, anyone could delete anyone else's temp files, which would be chaos.

You set it with:

Bash
chmod +t /some/shared/directory

# Or in octal, it's a leading 1
chmod 1777 /some/shared/directory

The other special bits are SUID (setuid) and SGID (setgid). SUID makes a binary run as its owner instead of the user executing it — this is how passwd can write to /etc/shadow even though you're not root. SGID does the same for group ownership, and on directories it forces new files to inherit the directory's group.

SUID is the most security-sensitive permission bit, and you should audit any SUID binary you don't recognize. A SUID binary owned by root runs with root's privileges no matter who starts it, so a bug in it, or a program that lets you run other commands, can give an attacker root.

Common Permission Patterns

The commands you'll use most often:

Captured terminal output: Common permission patterns with chmod
After chmod 755 the script has its x bits and runs. Captured on Ubuntu 26.04.1 under WSL 2.
Bash
# Regular files (documents, configs)
chmod 644 filename.txt # Owner can edit, everyone can read

# Scripts I want to run
chmod 755 script.sh # Owner can edit, everyone can run

# Private files (SSH keys, passwords)
chmod 600 ~/.ssh/id_ed25519 # Only owner can read/write, nobody else

# Private directories
chmod 700 ~/.ssh/ # Only owner can access at all

# Web server files
chmod 644 index.html # Everyone can read
chmod 755 /var/www/mysite/ # The directory needs execute to enter

OpenSSH checks these modes itself: it refuses a private key that others can read, and by default (StrictModes) the server ignores authorized_keys if the file or ~/.ssh is writable by others. Locking Down SSH covers key setup end to end. For web roots, Nginx Web Server Setup shows the ownership layout that goes with these modes. See chmod(1) for the full syntax, including symbolic forms like u+x and g-w.

A Directory Shared by a Group

A common real need behind chmod 777 is "several people (or a person and a service) have to write files in the same directory." A group and the SGID bit handle that without opening the directory to everyone:

Bash
# Create the group and add yourself (takes effect at your next login)
sudo groupadd developers
sudo usermod -aG developers "$USER"

# Directory owned by root, group developers, mode 2775:
# rwx for owner and group, r-x for others, SGID so new files inherit the group
sudo mkdir -p /srv/shared
sudo chown root:developers /srv/shared
sudo chmod 2775 /srv/shared

Check it worked: stat -c '%A %a %U:%G' /srv/shared should print drwxrwsr-x 2775 root:developers; the s in the group's execute position is the SGID bit. After logging out and back in, id lists developers, and a file you create with touch /srv/shared/test belongs to the developers group. Use 2770 instead if other users shouldn't even list the directory.

New files only get group write if the creating process's umask allows it (next section). When a single extra user or service account needs access and a group doesn't fit, an ACL adds just that entry. ACLs need the acl package, which most distributions install by default:

Bash
# Give www-data read and enter rights on the directory, and on files created in it later
sudo setfacl -m u:www-data:rx /srv/shared
sudo setfacl -d -m u:www-data:rx /srv/shared

# Show the ACL; ls -l marks files that have one with a trailing +
getfacl /srv/shared

See setfacl(1) for the full syntax. The -d form sets a default ACL, which only applies to files and directories created afterwards.

Default Permissions and umask

New files don't start at 777. A program asks for a mode when it creates a file (usually 666 for files and 777 for directories), and the kernel removes the bits set in the process's umask. With the common umask 022, new files come out 644 and new directories 755; with 002, they come out 664 and 775, which keeps group write and suits shared directories like the one above.

Captured terminal output: umask and the default mode of new files
With umask 077, a new file is created as 600, readable only by you. Captured on Ubuntu 26.04.1 under WSL 2.
Bash
# Show the current umask, as octal and as the permissions it leaves
umask
umask -S

# Change it for this shell session only
umask 002

A service started by systemd gets its umask from the unit's UMask= setting (default 0022), not from your shell, so set it there when a service creates files a group needs to write. The umask(2) page describes the rule.

Debugging Permission Issues

When something isn't working and you suspect permissions, work through it in this order:

  1. Check the file AND every parent directory. namei -l /var/www/mysite/index.html (from util-linux) lists the owner and mode of each component of the path in one go; you need execute permission on every directory in it.
  2. Check who the process is running as. A web server runs as www-data, not as you. Use ps aux | grep nginx or similar, then test as that user: sudo -u www-data test -r /path/to/file && echo readable.
  3. Check if SELinux or AppArmor is blocking you. On Fedora/RHEL, run getenforce. If it says "Enforcing", that's a whole other layer of security that might be stopping you even with correct Unix permissions; ls -Z shows a file's SELinux label. On Ubuntu, sudo aa-status lists AppArmor profiles.
  4. Check group membership. Adding a user to a group only takes effect in new login sessions; id username shows the configured groups, id on its own shows what your current session has. The same applies to the docker group in the Docker Installation Guide.
  5. Look at the actual error message. "Permission denied" (EACCES) usually means the mode bits, ACLs or a security module. "Operation not permitted" (EPERM) often means the operation needs ownership or root regardless of mode, or the file has an attribute such as immutable; lsattr file shows those, and getfacl file shows ACL entries that ls -l only hints at with a +.

Auditing SUID Files

List every SUID file on the root filesystem, with its owner and mode:

Bash
sudo find / -xdev -type f -perm -4000 -exec ls -l {} +

-perm -4000 matches files with the SUID bit set, and -xdev keeps find on one filesystem so it doesn't wander into /proc or network mounts; repeat the command for other mounted filesystems you care about. On a typical system, the list holds programs such as passwd, su, sudo, mount and umount. For each entry, check that a package installed it: dpkg -S /usr/bin/passwd on Debian and Ubuntu, rpm -qf /usr/bin/passwd on Fedora and RHEL, pacman -Qo /usr/bin/passwd on Arch. A SUID file that no package owns, especially one in a home directory or under /tmp, needs explaining. Replace -4000 with -2000 to list SGID files the same way.

Cleaning Up

If you created the practice group and directory from the shared-directory example, remove them, and take yourself out of the group:

Bash
sudo rm -rf /srv/shared
sudo gpasswd -d "$USER" developers
sudo groupdel developers

If you changed ownership or modes on real files and want to go back, the listing you saved with find -printf has the original owner, group and octal mode for every path. Apply them with chown and chmod on the paths that differ. Remove any ACL entries you added with setfacl -x u:USERNAME FILE, or all of them with setfacl -b FILE.

Next Steps