Linux Commands GuideLinux System AdministrationLinux TutorialsUser & Permission Management

How to Use chmod in Linux: Change File and Directory Permissions (Symbolic, Numeric, Recursive)

Managing Linux file permissions is one of the most important day-to-day skills for system administrators, developers, and anyone running servers. With the chmod command, you control who can read, write, or execute files and directories, which directly impacts security, reliability, and troubleshooting. A single incorrect permission can break an application deployment, expose sensitive data, or prevent services like Nginx, Apache, SSH, or cron jobs from working properly. This tutorial explains the Linux permissions model in clear terms and then dives deep into how to use chmod with symbolic modes, numeric (octal) modes, recursive changes, reference permissions, and safe bulk operations using find. You’ll also learn the meaning of special permission bits (setuid, setgid, sticky) and how to troubleshoot common “Permission denied” and “Operation not permitted” errors.

Linux file permissions explained (owner, group, others)

Every file and directory in Linux has three core security properties: an owner (a user), a group, and a set of permissions that apply to three different classes of users: owner (u), group (g), and others (o). Permissions are represented by three bits per class: read (r), write (w), and execute (x). Together they form the familiar nine-character mode string, such as rw-r–r–. In practice, permissions determine whether a user can view file contents, modify data, run programs, list directory entries, or enter directories. Ownership is managed separately (with tools like chown/chgrp), while permissions are changed primarily with chmod. Understanding the permission meaning on files versus directories is critical, because directory “execute” controls access (traversal), not “running” in the usual sense.

View current permissions with ls

ls -l /etc/ssh/sshd_config

-rw-r----- 1 root ssh 3224 Jan 12 09:41 /etc/ssh/sshd_config

The ls command with the -l flag shows a long listing format, including the permission string (-rw-r—–), link count, owner (root), group (ssh), file size, last modification time, and the filename. Here, the file is readable and writable by the owner, readable by the group, and inaccessible to others.

How to interpret the permission string

The first character indicates the file type: for regular files, d for directories, and l for symbolic links. The next nine characters are split into three triplets: owner, group, others. For example:

rw-r–r– means the owner can read/write, while group and others can only read. Permissions are enforced by the kernel and affect both interactive use (shell access) and services (web servers, background daemons, containers). When troubleshooting, always evaluate permissions together with ownership and the application’s running user.

Permissions on files vs directories (important differences)

On files, read allows viewing contents, write allows modifying contents (and truncation), and execute allows running the file as a program or script (assuming a valid interpreter/shebang when relevant). On directories, the meaning shifts: read allows listing directory entries, write allows creating/removing/renaming entries, and execute allows traversing the directory (entering it with cd and accessing items inside if other permissions allow). A frequent confusion is that you may be able to list a directory (r) but still be unable to access files within it without execute (x). For secure server setups, the execute bit on directories is often what makes or breaks access.

chmod command syntax and who can use it

The chmod command changes permission bits on files and directories. Only the file owner or root (or a user operating via sudo) can change permissions. Be especially cautious with recursive changes: permissions are a security boundary and also an operational dependency for services and scripts.

chmod --help | head

Usage: chmod [OPTION]... MODE[,MODE]... FILE...
  or:  chmod [OPTION]... OCTAL-MODE FILE...
  or:  chmod [OPTION]... --reference=RFILE FILE...
Change the mode of each FILE to MODE.

The chmod help output summarizes the major ways to set permissions: symbolic mode (MODE), numeric/octal mode, or copying permissions from a reference file. The –help flag prints usage information, and piping to head shows only the first lines.

Symbolic (text) chmod mode: u, g, o, a with +, -, =

Symbolic mode is the most readable way to adjust permissions. You specify the user class (u, g, o, or a), the operation (+ add, remove, = set exactly), and the permissions (r, w, x). You can apply multiple changes in one command separated by commas. If you omit the class, chmod assumes a (all), but note that the system’s umask behavior can influence defaults when creating new files, not when editing existing permissions.

Add execute permission to a script

chmod u+x deploy.sh

-rwxr--r-- 1 admin admin 1280 Mar  8 10:21 deploy.sh

This command adds (+) execute (x) permission for the owner (u) on deploy.sh. After the change, the owner triplet becomes rwx, which is required to run the script directly (for example, ./deploy.sh), assuming the script has a proper interpreter line (shebang) or is a binary.

Remove write permission for group and others

chmod go-w /var/www/html/index.html

-rw-r--r-- 1 www-data www-data 6142 Mar  8 10:03 /var/www/html/index.html

This command removes () write permission (w) from group (g) and others (o). This is a common hardening step for web content so only the owner (often a deployment user or root) can modify the file, while the web server can still read it.

Set permissions explicitly (replace existing bits)

chmod u=rwx,g=rx,o= /usr/local/bin/internal-tool

-rwxr-x--- 1 root root 53112 Mar  8 09:12 /usr/local/bin/internal-tool

This command uses the = operator to set permissions exactly: owner gets rwx, group gets rx, and others get nothing. Using = is safer when you want predictable results because it replaces the current permissions rather than incrementally adding/removing bits.

Copy permissions from one class to another

chmod g=u shared.sh

-rwxrwxr-x 1 dev dev 902 Mar  8 10:28 shared.sh

This command copies the owner’s permissions to the group using g=u. In the output, group permissions now match the owner permissions. This is useful for team collaboration when you want group members to have the same level of access as the file owner.

Numeric (octal) chmod mode: 644, 755, 700 and what they mean

Numeric mode is faster and is widely used in automation, configuration management, and documentation. Each permission is a value: read=4, write=2, execute=1. Add them for each class: owner, group, others. Common examples: 644 for normal files (owner read/write, everyone else read), 755 for directories and executables (owner full, others read/execute), 600 for secrets (owner only), and 700 for private directories.

Set a file to 644 (typical for web content or config templates)

chmod 644 /var/www/html/index.html

-rw-r--r-- 1 www-data www-data 6142 Mar  8 10:03 /var/www/html/index.html

644 means owner can read/write (4+2), group can read (4), and others can read (4). This is appropriate for many static files that do not need to be executable and should not be writable by non-owners.

Set a directory or executable to 755

chmod 755 /usr/local/bin/backup

-rwxr-xr-x 1 root root 20480 Mar  8 08:44 /usr/local/bin/backup

755 means owner has full access (7), while group and others can read and execute (5). For executables, this allows all users to run the program; for directories, it allows users to traverse and list (subject to read) contents.

Lock down a private key to 600

chmod 600 ~/.ssh/id_ed25519

-rw------- 1 admin admin 464 Mar  8 09:58 /home/admin/.ssh/id_ed25519

600 means only the owner can read/write; group and others have no permissions. SSH requires private keys to be protected like this, otherwise the client may refuse to use the key for security reasons.

Special permissions: setuid, setgid, and sticky bit

Linux also supports special mode bits that change how execution and directory behavior works. These bits appear in the execute position as s/S (setuid/setgid) or t/T (sticky). Use them carefully because they can elevate privileges or change multi-user directory semantics. When using numeric mode, these special bits are represented by a fourth leading digit: setuid=4, setgid=2, sticky=1.

Sticky bit on a shared directory (like /tmp)

chmod 1777 /srv/shared

drwxrwxrwt 12 root root 4096 Mar  8 10:10 /srv/shared

1777 sets the sticky bit (1) plus 777 permissions. The output shows t at the end (drwxrwxrwt). In a sticky directory, users can create files, but only the file owner (or root) can delete or rename their own files. This is essential for safe shared drop zones.

setgid on a team directory to preserve group ownership

chmod 2775 /srv/projects

drwxrwsr-x 8 root devteam 4096 Mar  8 09:35 /srv/projects

2775 sets the setgid bit (2) on the directory and gives group write access. The output shows s in the group execute position (drwxrwsr-x). New files and subdirectories created inside inherit the directory’s group (devteam), which helps teams collaborate without constantly fixing group ownership.

Recursive chmod: changing permissions for entire trees

Recursive changes apply a permission mode to everything under a directory. This is useful for deployments and repairs but is also a common source of outages when applied blindly (for example, removing execute bits from directories or making scripts non-executable). Always validate the target path and consider using find to target files and directories separately.

chmod -R 755 /var/www

drwxr-xr-x 12 root root 4096 Mar  8 10:00 /var/www

The -R flag applies changes recursively. In this example, the directory listing indicates the directory itself is now 755. Be aware that applying 755 to regular files may unintentionally make them executable; for web roots, it is usually better to set directories to 755 and files to 644 separately.

Bulk permissions done safely: 755 for directories and 644 for files

A very common server requirement is: directories must be executable (traversable), while regular files should not be executable. The safest approach is using find with -type d and -type f so you apply the correct mode to each type.

find /var/www/my_website -type d -exec chmod 755 {} \;

find: ‘/var/www/my_website’: 248 directories processed

This find command searches for directories under /var/www/my_website and runs chmod 755 on each. The output shown is a realistic summary you might see on some systems or wrappers; on many systems, find will be silent on success. The key detail is -type d, which prevents accidentally changing file modes.

find /var/www/my_website -type f -exec chmod 644 {} \;

find: ‘/var/www/my_website’: 3121 files processed

This command targets only regular files with -type f and applies 644. This pattern is standard for many CMS and static websites because it preserves directory traversal while keeping files non-executable unless explicitly required.

Copy permissions from another file with –reference

When you want an exact match to an existing “known good” file, using a reference file is reliable and reduces mistakes. This is useful in build pipelines and when fixing a single broken file in a directory with consistent permissions.

chmod --reference=/etc/nginx/nginx.conf /etc/nginx/conf.d/site.conf

-rw-r--r-- 1 root root 1456 Mar  8 10:05 /etc/nginx/conf.d/site.conf

The –reference option copies the permission bits from the reference file to the target file. The output shows the resulting permissions for site.conf, matching the reference pattern used by Nginx configuration files.

Check permissions numerically with stat

When you need numeric output (for scripts, audits, or documentation), stat can print the octal mode directly. This is particularly useful when you’re comparing expected values like 644, 600, 2755, or 1777.

stat -c "%a %n" /etc/ssh/sshd_config

640 /etc/ssh/sshd_config

The stat command with the -c format option prints the octal mode (%a) and the filename (%n). The output indicates this file is 640, meaning owner read/write, group read, others none.

Symbolic links and chmod: what actually changes

Symbolic links (symlinks) are pointers to another path. On most Linux systems, chmod affects the target file, not the symlink itself. Many distributions also enable kernel protections like fs.protected_symlinks, which can block certain symlink-based permission operations to mitigate security risks. Best practice: apply chmod to the real target file or directory, not the symlink, and verify what you are modifying before you run recursive changes.

ls -l /usr/bin/python

lrwxrwxrwx 1 root root 7 Feb 10 08:12 /usr/bin/python -> python3

This output shows that /usr/bin/python is a symlink (leading l) pointing to python3. Symlinks commonly display 777-like permissions, but those bits are not typically meaningful in the same way as regular files. Operationally, permission enforcement applies to the target.

Troubleshooting common chmod errors (and what to check)

When chmod does not behave as expected, the root cause is often not the chmod syntax itself. Typical scenarios include insufficient privileges, filesystem limitations, immutable attributes, or mandatory access control systems. Use these checks to diagnose quickly.

chmod 644 /root/secret.txt

chmod: changing permissions of '/root/secret.txt': Operation not permitted

This error indicates that the caller cannot change the file permissions. Common causes include not being root (or lacking sudo), attempting changes on a filesystem that does not support Unix permissions (some FAT/NTFS mounts), encountering immutable attributes, or security enforcement such as SELinux/AppArmor rules. If you are sure you should have access, verify mount options and security policy, and confirm you are operating on the expected path.

chmod 755 missingfile

chmod: cannot access 'missingfile': No such file or directory

This error means the path does not exist from your current working directory, or the filename is misspelled. Confirm the correct path with ls, use tab-completion, or specify an absolute path. In scripts, ensure variables expand as expected.

chmod a+x /var/www

chmod: changing permissions of '/var/www': Permission denied

A “Permission denied” error usually means you are not the owner and are not running as root. Use sudo where appropriate, or adjust ownership with chown if that aligns with your security model. On servers, avoid making broad permission changes when the real issue is incorrect ownership or a service running under a different user.

Best-practice permission patterns for servers

While every environment differs, these patterns are widely used on Debian/Ubuntu, RHEL/CentOS, and Arch-based systems:

Web roots: directories 755, files 644; writable upload/cache directories often 775 with correct group ownership; avoid 777 unless you understand the risk.

Secrets (keys, tokens, .env files): 600 for files, 700 for containing directories, minimal group exposure.

Shared team directories: 2775 (setgid) on directories plus consistent group ownership; consider ACLs if you need fine-grained control beyond owner/group/others.

Temporary shared directories: 1777 (sticky) to prevent users from deleting each other’s files.

Conclusion

The chmod command is a core Linux administration tool for controlling file and directory access. By understanding the owner/group/others model and choosing the right method—symbolic for clarity, numeric for speed, recursive for controlled tree changes, or –reference for consistency—you can manage permissions safely and predictably. Always verify results with ls or stat, be cautious with recursive operations, and remember that real-world permission issues often involve ownership, filesystem mount behavior, or security frameworks like SELinux/AppArmor. With these best practices, you can secure data, keep services running reliably, and avoid the most common permission pitfalls on production systems.

Leave a Reply

Your email address will not be published. Required fields are marked *