Every file and directory on a Linux system has a set of permissions that control who can read, write, or execute it. Understanding file permissions is one of the most fundamental skills for anyone working with Linux — whether you are a developer deploying code, a sysadmin managing servers, or a DevOps engineer writing automation scripts.
Misconfigured permissions are one of the most common causes of security vulnerabilities and "permission denied" errors. This guide walks you through everything you need to know — from the basics to advanced concepts like special bits and ACLs.
The Permission Model: Users, Groups, and Others
Linux uses a three-tier permission model. Every file is owned by a user (the file's creator, by default) and a group. Permissions are then defined for three classes of users:
u — User (owner): the account that owns the file
g — Group: users who belong to the file's group
o — Others: everyone else on the system
When you run ls -l, the first column shows the permission string. For example, -rwxr-xr-- means the owner can read, write, and execute; the group can read and execute; others can only read.
Read, Write, and Execute — What Each Permission Does
| Bit | Symbol | Octal | On Files | On Directories |
|---|---|---|---|---|
| Read | r | 4 | View file contents | List directory contents |
| Write | w | 2 | Modify file contents | Create/delete files in dir |
| Execute | x | 1 | Run as a program | Enter (cd into) directory |
A common mistake is thinking that "read" permission on a directory lets you read its files. It only lets you list the filenames. To actually read the files inside, you need read permission on those individual files and execute permission on the directory to traverse into it.
Numeric (Octal) vs. Symbolic Notation
Permissions can be expressed in two ways. Octal notation uses three digits (e.g., 755), where each digit is the sum of 4 (read) + 2 (write) + 1 (execute). Symbolic notation uses letters (e.g., rwxr-xr-x).
# Octal — set all permissions at once
$ chmod 755 deploy.sh
# Symbolic — add/remove specific bits
$ chmod u+x deploy.sh # add execute for owner
$ chmod go-w config.yml # remove write for group & others
Use octal when you want to set the full permission from scratch. Use symbolic when you want to tweak one specific bit without affecting the rest. In scripts and automation, octal is more common because it's explicit and unambiguous.
Changing Ownership with chown and chgrp
Permissions define what can be done; ownership defines who the permissions apply to. Thechown command changes the owner and group of a file:
$ chown www-data:www-data /var/www/html/index.html
$ chown -R deploy:deploy /opt/myapp/ # recursive
$ chgrp docker /var/run/docker.sock
On production servers, ownership mistakes are a top source of deployment failures. If your web server runs aswww-data but your deploy process creates files owned byroot, you will get "permission denied" errors even though the permission bits look correct.
Special Bits: Setuid, Setgid, and Sticky Bit
Beyond the standard rwx bits, Linux has three special permission bits that serve critical security and collaboration purposes:
Setuid (4xxx)
When set on an executable, it runs with the privileges of the file's owner, not the user who launched it. This is how passwd can modify/etc/shadow even when run by a normal user. Use sparingly — setuid root binaries are a major attack surface.
Setgid (2xxx)
On files, it works like setuid but for the group. On directories, it's more useful: new files created inside automatically inherit the directory's group, instead of the creator's primary group. This is essential for shared project directories where multiple users need to collaborate.
Sticky Bit (1xxx)
On directories, only the file owner (or root) can delete or rename files inside — even if others have write permission on the directory. The classic example is /tmp, which is world-writable but uses the sticky bit so users cannot delete each other's files.
$ chmod 4755 /usr/local/bin/myapp # setuid
$ chmod 2775 /srv/shared-project/ # setgid on dir
$ chmod 1777 /tmp/ # sticky bit
The umask: Default Permissions for New Files
When a process creates a new file, the kernel applies a umask to determine the default permissions. The umask is a bitmask that removes permissions. For example, a umask of022 means new files get644 (rw-r--r--) and new directories get755 (rwxr-xr-x).
$ umask # view current umask
0022
$ umask 027 # more restrictive: no access for others
On shared servers, setting a restrictive umask (like 077) prevents newly created files from being readable by other users. Set it in /etc/profile or~/.bashrc to make it persistent.
Access Control Lists (ACLs): Beyond the Basics
The standard permission model only supports one owner and one group per file. When you need finer-grained control — for example, giving a specific user read access without changing the file's group — you need POSIX ACLs.
# Grant user "alice" read access to a file
$ setfacl -m u:alice:r /var/log/app.log
# View ACL entries
$ getfacl /var/log/app.log
# Set default ACL on a directory (inherited by new files)
$ setfacl -d -m g:devops:rw /srv/project/
ACLs are particularly useful in environments with multiple teams sharing directories, CI/CD pipelines where service accounts need specific access, or when migrating from Windows where NTFS-style granular permissions were in use. Note that the filesystem must be mounted with ACL support (most modern distros enable this by default).
Common Permission Patterns and When to Use Them
| Octal | Symbolic | Use Case |
|---|---|---|
| 644 | rw-r--r-- | Config files, HTML, CSS, static assets |
| 755 | rwxr-xr-x | Executable scripts, application directories |
| 600 | rw------- | SSH private keys, secrets, .env files |
| 700 | rwx------ | ~/.ssh directory, private script dirs |
| 775 | rwxrwxr-x | Shared project directories (with setgid) |
| 400 | r-------- | Read-only secrets (AWS credentials, TLS keys) |
Troubleshooting Permission Issues
When you encounter "Permission denied" errors, follow this checklist:
1. Check the file permissions: ls -la /path/to/file
2. Check ownership: Does the user/group match who is trying to access it?
3. Check parent directories: You need execute permission on every directory in the path. A file at /opt/app/data/file.txt requires +x on /opt, /opt/app, and /opt/app/data.
4. Check for ACLs: getfacl /path/to/file
5. Check SELinux/AppArmor: Mandatory access controls can deny access even when traditional permissions allow it. Run getenforce and check /var/log/audit/audit.log.
The most common gotcha on production servers is forgetting about parent directory permissions. Even if a file ischmod 777, it is inaccessible if a parent directory does not have execute permission for the requesting user.