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
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
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.
-
644meansrw-r--r--: owner can read and write; everyone else can read. -
600meansrw-------: only the owner can read and write. -
755meansrwxr-xr-x: owner can read, write, and execute; others can read and execute. -
700meansrwx------: 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
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
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
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
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 {} +
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"
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
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
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)