Tools: Understanding Unix File Permissions: A Practical Guide

Tools: Understanding Unix File Permissions: A Practical Guide

The Owner / Group / Other Permission Model

How Ownership Is Determined

Reading Permission Output

Octal vs Symbolic Notation: A Complete Comparison

Octal (Numeric) Notation

Symbolic Notation

When to Use Which?

Common Permission Patterns

755 — Standard for Executables and Directories

644 — Standard for Regular Files

600 — Private Files

400 — Read-Only Private Files

700 — Private Directories

750 — Group-Shared Directories

The chmod Command in Depth

Octal Mode

Symbolic Mode

Useful chmod Flags

Directory vs File Permissions

Special Permissions: Setuid, Setgid, and Sticky Bit

Setuid (4000) — Run as File Owner

Setgid (2000) — Run as File Group or Inherit Group

Sticky Bit (1000) — Restrict Deletion

Combining Special Permissions

Step-by-Step Tutorial: Using the DevToolkit Chmod Calculator

Security Best Practices: The Principle of Least Privilege

For Web Applications

For SSH

For Application Secrets

Common Mistakes and Troubleshooting

Mistake 1: chmod 777 Everything

Mistake 2: Recursive chmod on Mixed Content

Mistake 3: Forgetting Group Permissions

Mistake 4: Ignoring umask

Mistake 5: Not Using Groups Effectively

Troubleshooting "Permission Denied" Errors

umask: Controlling Default Permissions

Frequently Asked Questions

What does chmod 755 mean?

What is the difference between chmod 644 and 755?

Why is chmod 777 dangerous?

How do I make a file executable in Linux?

What permissions should .env files have?

How do I check current file permissions?

What is the difference between chmod and chown?

Can I undo a chmod command?

Conclusion File permissions are one of the first things that trip up developers on Linux and macOS. You get a "Permission denied" error, frantically Google "chmod 777", fix it, and move on — without really understanding what happened. This guide gives you a practical understanding of Unix permissions that will save you from both "Permission denied" errors and accidental security holes. Whether you are deploying web applications, managing SSH keys, or scripting server automation, having chmod permissions explained clearly will make you a more effective developer. Every file and directory on a Unix system has three sets of permissions for three classes of users: Each class can have three permissions: This three-by-three model creates a total of nine permission bits. Understanding how these bits interact is the key to managing file security on any Unix-like operating system. When you create a file, the operating system automatically sets you as the owner. The file's group is typically set to your primary group (which you can check with the id command). You can change ownership with chown and group with chgrp: Only the root user (or a user with sudo) can change file ownership. Regular users can only change the group to another group they belong to. When you run ls -la, you see permissions like -rwxr-xr-x. Here's how to read it: Each position is either the permission letter or a dash. Dashes mean "not granted." The file type character at the beginning is not a permission — it tells you what kind of filesystem entry you are looking at: There are two ways to express Unix file permissions: octal (numeric) and symbolic. Both are used with the chmod command, and each has advantages depending on the situation. The octal system represents each permission set as a single digit from 0 to 7. Each permission has a numeric value: So -rwxr-xr-x = 755. Three digits: owner, group, others. Here is the full reference table for a single digit: Symbolic notation uses letters and operators to modify permissions. It is more flexible when you want to change specific bits without affecting others: The user classes are: Use octal notation when you want to set all permissions at once and know exactly what the result should be. It is concise and unambiguous — chmod 644 file always produces the same result regardless of the file's current permissions. Use symbolic notation when you want to modify one specific permission without touching the rest. For example, chmod +x script.sh adds execute without changing any read or write bits. Use our Chmod Calculator to convert between octal, symbolic, and see the chmod command ready to copy. If you are working with number conversions in other contexts, our Number Base Converter can help with octal-to-binary and other base conversions. Here is a comprehensive reference table of the most common chmod permission patterns, what they mean, and when to use them: Owner can read, write, and execute. Everyone else can read and execute but not modify. This is the default for directories and executable scripts. Owner can read and write. Everyone else can only read. This is the default for most files. Only the owner can read and write. No access for anyone else. Use this for sensitive files like environment variables, database credentials, and private keys. If you are generating credentials, our Password Generator can create strong, random passwords. Owner can only read. SSH actually requires this — it refuses to use private keys with more permissive settings. If you get "Permissions 0644 for '~/.ssh/id_rsa' are too open", this is the fix. Only the owner can access the directory at all. Others can't even list its contents. Owner has full access, group members can read and enter the directory, and others have no access at all. This is ideal for team projects on shared servers. Symbolic mode lets you add or remove specific permissions without changing others: The same permission bits mean different things for files and directories: A common gotcha: a directory needs execute permission for users to access files inside it, even if those files have read permission. Without x on the directory, users can't reach the files. This is why directories almost always have execute permission (755 or 700) while regular files do not (644 or 600). Another subtlety: having write permission on a directory means you can delete any file in that directory, even files you do not own and cannot read. This is why the sticky bit (covered below) exists. Beyond the basic nine rwx bits, Unix has three special permission bits that control advanced behavior. These are represented by an optional fourth digit prepended to the octal notation. When the setuid bit is set on an executable file, the program runs with the permissions of the file's owner, not the user executing it. This is how the passwd command works — it is owned by root and has setuid set, which allows regular users to modify the /etc/shadow file that only root can write to. Notice the s in the owner's execute position. A lowercase s means both setuid and execute are set. An uppercase S means setuid is set but execute is not (which is usually a misconfiguration). Security warning: Setuid programs are a common attack vector. Never set setuid on scripts, and audit any setuid binaries on your system regularly. On executable files, setgid works like setuid but for the group — the program runs with the file's group permissions. On directories, setgid has a different and very useful behavior: new files and subdirectories created inside inherit the directory's group instead of the creating user's primary group. This is invaluable for team collaboration. Without setgid, files created by different users would belong to each user's primary group, making shared access unpredictable. The sticky bit on a directory prevents users from deleting files they do not own. Without it, anyone with write permission on the directory can delete any file inside it. The classic example is /tmp: The t at the end indicates the sticky bit. With this set, anyone can create files in /tmp, but only the file's owner (or root) can delete them. This prevents users from deleting each other's temporary files. Special permissions are added as a leading digit in octal notation: Use our Chmod Calculator to experiment with these special permission bits visually. If calculating octal values in your head feels error-prone, the DevToolkit Chmod Calculator makes it visual and instant. Here is how to use it: This is especially useful when you are configuring server deployments, writing CI/CD scripts, or setting up cron jobs that need specific file permissions. If you are building deployment scripts, you might also find our Regex Tester helpful for parsing log files and our JSON Formatter useful for reading configuration files. The golden rule of file permissions is the principle of least privilege: grant only the minimum permissions needed for a file or directory to function correctly. Here are concrete guidelines: To generate secure credentials for these files, use our Password Generator. For encoding sensitive data in configuration, our Base64 Encoder and Hash Generator can be useful. If you are working with JWT tokens, the JWT Decoder helps verify token contents without exposing secrets. chmod 777 gives everyone full access. This is a security risk and usually means you don't understand what permission is actually needed. Instead: Running chmod -R 755 . gives everything execute permission — including text files, images, and configs that should never be executable. Instead, set files and directories separately: On shared servers, group permissions matter. If multiple users need to edit files in a shared directory, set the group appropriately: The umask controls the default permissions for newly created files. A umask of 022 means new files get 644 and new directories get 755. A umask of 077 means new files get 600 and directories get 700. If your files always seem to have the wrong permissions, check your umask: The umask is subtracted from the maximum permissions (666 for files, 777 for directories). So with a umask of 027, new files get 640 and new directories get 750. Instead of loosening permissions with chmod 777, add users to appropriate groups and use group permissions: When you encounter "Permission denied", follow this diagnostic checklist: While chmod changes existing permissions, umask controls what permissions new files and directories receive. Understanding umask prevents you from having to chmod every new file. The umask value is a mask that is subtracted from the maximum permissions. Files have a maximum of 666 (no execute by default) and directories have a maximum of 777: Set your umask in ~/.bashrc or ~/.profile for persistent configuration. For servers handling sensitive data, a umask of 077 is a good baseline. chmod 755 sets the file to be readable, writable, and executable by the owner, and readable and executable (but not writable) by the group and others. In symbolic notation, this is rwxr-xr-x. It is the standard permission for executable scripts and directories. The difference is the execute bit. 644 (rw-r--r--) does not grant execute permission to anyone — use this for regular files like HTML, CSS, images, and config files. 755 (rwxr-xr-x) grants execute permission — use this for scripts, binaries, and directories (which need execute for users to enter them). chmod 777 grants read, write, and execute permissions to every user on the system. This means any user or process can modify or delete the file. On a web server, this could allow an attacker to inject malicious code. Always use the minimum permissions required. Use chmod +x filename to add execute permission for all users, or chmod u+x filename to add it only for the owner. For scripts, you also need a shebang line (e.g., #!/bin/bash) at the top of the file. Environment files containing secrets (API keys, database passwords, etc.) should have 600 permissions — only the owner can read and write. Never commit .env files to version control either; add them to your .gitignore. Use ls -la filename to see permissions in symbolic format, or stat filename for more detailed information including the octal representation. You can also use our Chmod Calculator to decode octal values into human-readable format. chmod changes what actions (read, write, execute) are allowed. chown changes who owns the file. Often you need both — for example, setting the correct owner and the correct permissions for a web server to access files properly. There is no built-in undo for chmod. If you accidentally change permissions and do not remember the original values, check your backup or use chmod --reference=similar_file broken_file to copy permissions from a known-good file. This is why it is always a good idea to check permissions with ls -la before changing them. Working with Unix systems involves more than just file permissions. Here are other DevToolkit tools that complement your workflow: File permissions are simple once you understand the model: three user classes, three permission types, three digits. With the addition of special permissions (setuid, setgid, sticky bit), umask defaults, and group management, you have a complete toolkit for securing files on any Unix-like system. Remember the essentials: use 644 for regular files, 755 for directories and scripts, 600 for sensitive files, and never use 777 unless you genuinely want every user on the system to have full access. When in doubt, follow the principle of least privilege — start restrictive and loosen only as needed. Need to calculate permissions quickly? Our Chmod Calculator lets you toggle permissions visually and copies the chmod command to your clipboard — no mental math required. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to ? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ chown alice file.txt # Change owner to alice chgrp developers file.txt # Change group to developers chown alice:developers file.txt # Change both at once chown alice file.txt # Change owner to alice chgrp developers file.txt # Change group to developers chown alice:developers file.txt # Change both at once chown alice file.txt # Change owner to alice chgrp developers file.txt # Change group to developers chown alice:developers file.txt # Change both at once - rwx r-x r-x │ │ │ │ │ │ │ └── Others: read + execute (no write) │ │ └────── Group: read + execute (no write) │ └────────── Owner: read + write + execute └──────────── File type (- = file, d = directory, l = symlink) - rwx r-x r-x │ │ │ │ │ │ │ └── Others: read + execute (no write) │ │ └────── Group: read + execute (no write) │ └────────── Owner: read + write + execute └──────────── File type (- = file, d = directory, l = symlink) - rwx r-x r-x │ │ │ │ │ │ │ └── Others: read + execute (no write) │ │ └────── Group: read + execute (no write) │ └────────── Owner: read + write + execute └──────────── File type (- = file, d = directory, l = symlink) chmod u+x script.sh # Add execute for owner chmod go-w config.json # Remove write from group and others chmod a+r README.md # Add read for all users chmod u=rwx,g=rx,o=r file # Set exact permissions chmod u+x script.sh # Add execute for owner chmod go-w config.json # Remove write from group and others chmod a+r README.md # Add read for all users chmod u=rwx,g=rx,o=r file # Set exact permissions chmod u+x script.sh # Add execute for owner chmod go-w config.json # Remove write from group and others chmod a+r README.md # Add read for all users chmod u=rwx,g=rx,o=r file # Set exact permissions chmod 644 file chmod +x script.sh chmod 755 script.sh chmod 755 /var/www/html chmod 755 script.sh chmod 755 /var/www/html chmod 755 script.sh chmod 755 /var/www/html chmod 644 index.html chmod 644 config.json chmod 644 index.html chmod 644 config.json chmod 644 index.html chmod 644 config.json chmod 600 .env chmod 600 credentials.json chmod 600 .env chmod 600 credentials.json chmod 600 .env chmod 600 credentials.json chmod 400 ~/.ssh/id_rsa chmod 400 ~/.ssh/id_rsa chmod 400 ~/.ssh/id_rsa chmod 700 ~/.ssh chmod 700 ~/private chmod 700 ~/.ssh chmod 700 ~/private chmod 700 ~/.ssh chmod 700 ~/private chmod 750 /var/www/project chgrp developers /var/www/project chmod 750 /var/www/project chgrp developers /var/www/project chmod 750 /var/www/project chgrp developers /var/www/project chmod 755 file.sh # Set exact permissions chmod -R 644 ./docs # Recursive (all files in docs/) chmod 755 file.sh # Set exact permissions chmod -R 644 ./docs # Recursive (all files in docs/) chmod 755 file.sh # Set exact permissions chmod -R 644 ./docs # Recursive (all files in docs/) chmod +x script.sh # Add execute for everyone chmod u+w file.txt # Add write for owner chmod go-w file.txt # Remove write for group and others chmod a+r file.txt # Add read for all (a = all) chmod u=rwx,go=rx dir # Set exact: owner=rwx, group+others=rx chmod +x script.sh # Add execute for everyone chmod u+w file.txt # Add write for owner chmod go-w file.txt # Remove write for group and others chmod a+r file.txt # Add read for all (a = all) chmod u=rwx,go=rx dir # Set exact: owner=rwx, group+others=rx chmod +x script.sh # Add execute for everyone chmod u+w file.txt # Add write for owner chmod go-w file.txt # Remove write for group and others chmod a+r file.txt # Add read for all (a = all) chmod u=rwx,go=rx dir # Set exact: owner=rwx, group+others=rx --reference=FILE chmod --reference=known-good.conf new.conf # Match permissions from another file chmod -Rv 755 /var/www/html/ # Recursive with verbose output chmod --reference=known-good.conf new.conf # Match permissions from another file chmod -Rv 755 /var/www/html/ # Recursive with verbose output chmod --reference=known-good.conf new.conf # Match permissions from another file chmod -Rv 755 /var/www/html/ # Recursive with verbose output /etc/shadow chmod 4755 /usr/bin/passwd # ls -la shows: -rwsr-xr-x chmod 4755 /usr/bin/passwd # ls -la shows: -rwsr-xr-x chmod 4755 /usr/bin/passwd # ls -la shows: -rwsr-xr-x # Set setgid on a shared directory chmod 2775 /var/www/shared chgrp webdevs /var/www/shared # Now any file created inside /var/www/shared # automatically belongs to the "webdevs" group # Set setgid on a shared directory chmod 2775 /var/www/shared chgrp webdevs /var/www/shared # Now any file created inside /var/www/shared # automatically belongs to the "webdevs" group # Set setgid on a shared directory chmod 2775 /var/www/shared chgrp webdevs /var/www/shared # Now any file created inside /var/www/shared # automatically belongs to the "webdevs" group # ls -la shows: drwxrwsr-x # Note the 's' in the group execute position # ls -la shows: drwxrwsr-x # Note the 's' in the group execute position # ls -la shows: drwxrwsr-x # Note the 's' in the group execute position chmod 1777 /tmp # ls -la shows: drwxrwxrwt # Note the 't' in the others execute position chmod 1777 /tmp # ls -la shows: drwxrwxrwt # Note the 't' in the others execute position chmod 1777 /tmp # ls -la shows: drwxrwxrwt # Note the 't' in the others execute position chmod 4755 file # setuid + rwxr-xr-x chmod 2755 dir # setgid + rwxr-xr-x chmod 1777 dir # sticky + rwxrwxrwx chmod 6755 file # setuid + setgid + rwxr-xr-x chmod 4755 file # setuid + rwxr-xr-x chmod 2755 dir # setgid + rwxr-xr-x chmod 1777 dir # sticky + rwxrwxrwx chmod 6755 file # setuid + setgid + rwxr-xr-x chmod 4755 file # setuid + rwxr-xr-x chmod 2755 dir # setgid + rwxr-xr-x chmod 1777 dir # sticky + rwxrwxrwx chmod 6755 file # setuid + setgid + rwxr-xr-x chmod 755 filename ~/.ssh/authorized_keys ~/.ssh/id_rsa ~/.ssh/id_rsa.pub ~/.ssh/config chmod -R 755 . # Directories: 755 (need execute for cd) find . -type d -exec chmod 755 {} \; # Files: 644 (don't need execute) find . -type f -exec chmod 644 {} \; # Directories: 755 (need execute for cd) find . -type d -exec chmod 755 {} \; # Files: 644 (don't need execute) find . -type f -exec chmod 644 {} \; # Directories: 755 (need execute for cd) find . -type d -exec chmod 755 {} \; # Files: 644 (don't need execute) find . -type f -exec chmod 644 {} \; chgrp -R developers /var/www/project chmod -R 775 /var/www/project chgrp -R developers /var/www/project chmod -R 775 /var/www/project chgrp -R developers /var/www/project chmod -R 775 /var/www/project umask # Show current umask umask 022 # Set standard umask (files: 644, dirs: 755) umask 077 # Set restrictive umask (files: 600, dirs: 700) umask # Show current umask umask 022 # Set standard umask (files: 644, dirs: 755) umask 077 # Set restrictive umask (files: 600, dirs: 700) umask # Show current umask umask 022 # Set standard umask (files: 644, dirs: 755) umask 077 # Set restrictive umask (files: 600, dirs: 700) # Create a group for the project -weight: 600;">sudo groupadd webdevs # Add users to the group -weight: 600;">sudo usermod -aG webdevs alice -weight: 600;">sudo usermod -aG webdevs bob # Set the directory group and permissions chgrp -R webdevs /var/www/project chmod -R 2775 /var/www/project # setgid ensures new files inherit group # Create a group for the project -weight: 600;">sudo groupadd webdevs # Add users to the group -weight: 600;">sudo usermod -aG webdevs alice -weight: 600;">sudo usermod -aG webdevs bob # Set the directory group and permissions chgrp -R webdevs /var/www/project chmod -R 2775 /var/www/project # setgid ensures new files inherit group # Create a group for the project -weight: 600;">sudo groupadd webdevs # Add users to the group -weight: 600;">sudo usermod -aG webdevs alice -weight: 600;">sudo usermod -aG webdevs bob # Set the directory group and permissions chgrp -R webdevs /var/www/project chmod -R 2775 /var/www/project # setgid ensures new files inherit group ls -la /path/to/file namei -l /path/to/file getfacl /path/to/file chmod +x filename chmod u+x filename #!/bin/bash ls -la filename stat filename chmod --reference=similar_file broken_file - Owner (u) — the user who owns the file. By default, the user who creates a file becomes its owner. - Group (g) — users in the file's group. Every file is assigned to one group, and any user who is a member of that group gets these permissions. - Others (o) — everyone else on the system who is neither the owner nor in the file's group. - Read (r = 4) — view file contents, list directory contents - Write (w = 2) — modify file, create/delete files in directory - Execute (x = 1) — run file as program, access directory (cd into it) - - — regular file - d — directory - l — symbolic link - c — character device - b — block device - p — named pipe (FIFO) - r = 4, w = 2, x = 1 - Add them up: rwx = 4+2+1 = 7 - r-x = 4+0+1 = 5 - r-- = 4+0+0 = 4 - -w- = 0+2+0 = 2 - --x = 0+0+1 = 1 - --- = 0+0+0 = 0 - + — add the specified permission - - — -weight: 500;">remove the specified permission - = — set permissions exactly (removes anything not listed) - u — owner (user) - a — all (equivalent to ugo) - -R — apply recursively to all files and subdirectories - -v — verbose mode, shows each file being changed - -c — like verbose but only shows files that actually changed - --reference=FILE — copy permissions from another file - Read on directory = list contents (ls) - Write on directory = create/delete files inside it - Execute on directory = access directory (cd into it, access files by path) - Open the tool — Navigate to DevToolkit Chmod Calculator in your browser. - Toggle permission checkboxes — Click the read, write, and execute checkboxes for Owner, Group, and Others. Each click immediately updates the octal value and the symbolic representation. - Or type an octal number — If you already have a permission number like 644, type it in the octal input field. The checkboxes and symbolic notation -weight: 500;">update automatically. - Review the generated command — The tool generates the full chmod command (e.g., chmod 755 filename) ready to copy. - Copy to clipboard — Click the copy button and paste the command directly into your terminal. - Web root directory: 755 (owner: deploy user, group: www-data) - PHP/Python/application files: 644 - Upload directories: 775 (so the web server group can write) - Configuration files with secrets: 640 or 600 - Log files: 640 - Never use 777 on production servers - ~/.ssh/ directory: 700 - ~/.ssh/authorized_keys: 600 - ~/.ssh/id_rsa (private key): 400 - ~/.ssh/id_rsa.pub (public key): 644 - ~/.ssh/config: 600 - .env files: 600 - API key files: 600 - TLS/SSL certificates: 644 (public cert) or 600 (private key) - Database credential files: 600 - If a web server needs to read a file: 644 and make sure the web server user owns it (or is in the group) - If a script needs to be executable: 755 - If only your user needs access: 600 or 700 - Check the file permissions: ls -la /path/to/file - Check who you are: whoami and id - Check directory permissions: You need execute on every directory in the path. Run namei -l /path/to/file to see permissions on each component. - Check for ACLs: getfacl /path/to/file — Access Control Lists can override basic permissions. - Check SELinux/AppArmor: On systems with mandatory access control, regular permissions might not be enough. Check ls -Z for SELinux contexts. - Chmod Calculator — Visually set and convert file permissions - Cron Expression Generator — Build cron schedules for automated scripts (see our cron cheat sheet) - Regex Tester — Test patterns for log parsing and file matching (see our regex cheat sheet) - Hash Generator — Generate MD5, SHA-256, and other hashes for file integrity checks - Password Generator — Create strong passwords for system accounts - Base64 Encoder/Decoder — Encode credentials and certificates (learn more in our Base64 guide) - JSON Formatter — Format and validate server configuration files