DEV Community

Nate
Nate

Posted on Originally published at sshflow.com

Linux File Permissions: Use chmod Without Breaking Access

A Permission denied error does not always mean the file needs broader permissions. The cause might be a parent directory, the file’s owner, or an access control that chmod does not change. Before reaching for chmod 777, identify which user needs access and what operation they need to perform.

Read the mode before changing it

Start by checking the file, its owner, and the directory itself:

ls -l report.txt
ls -ld /srv/www/site
stat -c '%a %A %U:%G %n' report.txt
Enter fullscreen mode Exit fullscreen mode

In ls -l output, the first character identifies the object type (- for a regular file, d for a directory). The next nine characters describe permissions for the owner, group, and others, in that order. For example:

-rw-r--r-- 1 alice developers 128 Nov 11 10:00 report.txt
Enter fullscreen mode Exit fullscreen mode

Here, Alice can read and write the file; members of the group and other users can read it. The owner and group matter: chmod changes permission bits, not ownership.

stat -c is the GNU/Linux form shown here; its options may differ on macOS and BSD.

What the numbers mean

Each permission digit is a sum of three bits: read is 4, write is 2, and execute is 1. A mode has one digit each for owner, group, and others.

  • 644 means rw-r--r--: owner can read and write; everyone else can read.
  • 600 means rw-------: only the owner can read and write.
  • 755 means rwxr-xr-x: owner can read, write, and execute; others can read and execute.
  • 700 means rwx------: only the owner has access.

Apply a mode to a specific path, then verify it:

chmod 644 report.txt
stat -c '%a %A %n' report.txt
Enter fullscreen mode Exit fullscreen mode

These modes are examples, not universal defaults. A private file such as a secret should not be readable by everyone. For example, when chmod 600 is appropriate—and when it is not is especially relevant when setting permissions on private SSH keys.

If you only need to add or remove a particular permission, symbolic modes can be more precise than replacing the whole mode:

chmod u+x deploy.sh       # add execute for the owner
chmod g+w shared.txt      # add group write
chmod o-r private.txt     # remove read for others
chmod u=rw,g=r,o= config.ini  # set owner rw, group r, others none
Enter fullscreen mode Exit fullscreen mode

u, g, and o mean owner, group, and others. + and - change selected bits while leaving unrelated ones alone; = sets the specified class exactly. Thus chmod u+x deploy.sh does not also make the script executable by everyone else.

Directories have different rules

Read, write, and execute mean something different for a directory than for a regular file:

  • Read (r) lets a user list entry names.
  • Write (w) allows creating, deleting, or renaming entries, normally in combination with execute access.
  • Execute (x) allows traversing or searching the directory to access entries by name.

That last bit explains a common surprise: a file can be readable but still inaccessible if a parent directory does not allow traversal. Inspect the path components with:

namei -l /srv/www/site/index.html
Enter fullscreen mode Exit fullscreen mode

A mode like 755 is often used for a directory that should be traversable by other users, while 700 restricts it to its owner. Choose based on the users and processes that actually need access.

Be careful with recursive changes

chmod -R applies a change throughout a directory tree, including both files and directories. That can break access in either direction:

chmod -R 755 public/  # also marks regular files executable
chmod -R 644 public/  # removes directory traversal permission
Enter fullscreen mode Exit fullscreen mode

If you are certain a tree contains only public, non-executable files and ordinary directories, you can handle the two types separately. Preview the paths first and replace the example with a verified target:

ROOT="$HOME/site"
find "$ROOT" -type d -print
find "$ROOT" -type f -print

find "$ROOT" -type d -exec chmod 755 {} +
find "$ROOT" -type f -exec chmod 644 {} +
Enter fullscreen mode Exit fullscreen mode

This is not a general-purpose repair recipe. It would remove execute permission from scripts and could expose private files. For a narrower change that adds execute only to directories and files that already have some execute bit set, GNU/Linux supports uppercase X:

chmod -R u+X "$ROOT"
Enter fullscreen mode Exit fullscreen mode

Review the target before any recursive operation, especially on application or deployment directories.

When chmod does not fix it

First establish which account is doing the operation and which path it is trying to access. Then check ownership and the whole path:

id
ls -l /srv/app/config.ini
namei -l /srv/app/config.ini
Enter fullscreen mode Exit fullscreen mode

If the file belongs to the wrong account, changing its mode may not be the right fix. Ownership can be changed with chown, but confirm the intended owner and group before using it:

sudo chown appuser:appgroup /srv/app/config.ini
Enter fullscreen mode Exit fullscreen mode

If the mode and ownership look correct, other controls may be involved, such as ACLs, a read-only mount, or SELinux. On systems with ACL tools installed, getfacl /path/to/file can show additional entries. A trailing + in ls -l can indicate extended ACLs.

For remote file transfers, remember that permissions apply on the server’s filesystem. A local chmod does not change a remote file; the server account must be allowed to change it. If the error occurs during an SFTP transfer, this SFTP permission troubleshooting guide covers remote ownership, paths, and other causes.

Avoid chmod 777 as a general fix. It grants read, write, and execute or directory traversal access to owner, group, and others. On a regular file, world-write access can let unrelated local users modify it; on a directory, it can allow them to create or remove entries. It also does not resolve ownership problems, ACLs, or read-only mounts.

A safer workflow is simple: inspect the mode and owner, identify the account that needs access, make the smallest change on the specific path, and verify the result. When access still fails, investigate the path and other controls rather than making everything writable.

I originally published a more detailed version of this guide on the SSHFlow blog.

I'm also building SSHFlow — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.

Top comments (0)