| 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:
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:
-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 seed. - 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 + write5= 4 + 1 = read + execute4= just read0= 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:
# 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:
/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.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:
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:
chmod 755 the script has its x bits and runs. Captured on Ubuntu 26.04.1 under WSL 2.# 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:
# 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:
# 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.
umask 077, a new file is created as 600, readable only by you. Captured on Ubuntu 26.04.1 under WSL 2.# 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:
- 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. - Check who the process is running as. A web server runs as
www-data, not as you. Useps aux | grep nginxor similar, then test as that user:sudo -u www-data test -r /path/to/file && echo readable. - 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 -Zshows a file's SELinux label. On Ubuntu,sudo aa-statuslists AppArmor profiles. - Check group membership. Adding a user to a group only takes effect in new login sessions;
id usernameshows the configured groups,idon its own shows what your current session has. The same applies to thedockergroup in the Docker Installation Guide. - 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 fileshows those, andgetfacl fileshows ACL entries thatls -lonly hints at with a+.
Auditing SUID Files
List every SUID file on the root filesystem, with its owner and mode:
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:
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
- Locking Down SSH: the modes sshd insists on for
~/.sshandauthorized_keys. - Nginx Web Server Setup: ownership and modes for a web root.
- Running Your Scripts as Real Services with Systemd: running a service as its own user instead of root.
- Linux Package Management: finding which package installed a file, which helps when a mode looks wrong.



