Linux Permissions: chmod and chown
"Permission denied"
During my Linux newbie days, Permission denied haunted me everywhere.
Tried to read a file? Denied. Run a script? Denied. Even entering a directory? Denied.
My senior dev taught me a magic spell: "Just chmod 777 it when things don't work."
It worked like a charm. I thought that was the solution. (Until the Security Team ripped me apart.)
Looking back, I cringe. chmod 777 was basically "leaving my front door wide open with the password taped to the doorframe."
Why I studied this
Docker volume mounts kept throwing permission errors.
Couldn't write logs, couldn't read config files. I kept patching things with sudo, but I realized "If I don't understand this properly, I'll keep hitting the same wall."
So I dug into Linux permissions. What rwx means, what 755 signifies, the difference between chmod and chown. Once the fog cleared, I could look at an error message and instantly know, "Ah, permission issue."
What confused me initially
Running ls -l shows this:
-rw-r--r-- 1 ubuntu dev 1024 Feb 6 12:34 config.txt
drwxr-xr-x 2 ubuntu dev 4096 Feb 6 12:35 logs
It seemed like there was a pattern, but it looked like alien code.
What's -rw-r--r--? Why the number 1024? What's ubuntu dev?
The most confusing part: x permission on directories. "You need execute permission to enter a directory" didn't make sense at first. Directories aren't programs, so why do they need execute permission?
And why chmod 644 is fine but chmod 777 is dangerous wasn't immediately obvious.
How I finally understood it
The apartment building analogy clicked
Thinking of the Linux file system as an apartment building made everything clear.
- Owner: The apartment owner. Can decorate, add furniture, lock/unlock doors—full control.
- Group (same floor neighbors): People living on the same floor. They share the hallway vacuum cleaner but can't open each other's fridges.
- Others (outsiders): Delivery drivers, visitors. They can come to the front door but can't just walk into your living room.
This 3-tier structure is the essence of Linux permissions.
Understanding rwx
In Linux, there are exactly 3 actions you can perform on a file or directory:
- r (Read, 4): Read access
- File: Can view contents.
- Directory: Can list contents with
ls.
- w (Write, 2): Write access
- File: Can modify or delete.
- Directory: Can create or delete files inside.
- x (Execute, 1): Execute access
- File: Can run as a program.
- Directory: Can enter the directory (with
cd).
Without x permission on a directory, you can't cd into it. This clicked for me when I thought: "If you don't have entry clearance to an apartment hallway, you can't enter no matter what's inside."
The secret of numbers: 4+2+1 combinations
Converting rwx to numbers seemed magical at first, but it boiled down to this:
- r = 4
- w = 2
- x = 1
You just add them up.
rwx= 4 + 2 + 1 = 7 (read, write, execute all allowed)rw-= 4 + 2 + 0 = 6 (read, write only, no execute)r-x= 4 + 0 + 1 = 5 (read, execute only, no write)r--= 4 + 0 + 0 = 4 (read only)---= 0 + 0 + 0 = 0 (no access)
For example, 755 means:
- Owner: 7 (rwx) - full access
- Group: 5 (r-x) - read, execute allowed
- Others: 5 (r-x) - read, execute allowed
Conversely, when I see -rwxr-xr-x in ls -l, I immediately know: "Ah, 755 permissions."
Decoding ls -l output
$ ls -l
-rw-r--r-- 1 ubuntu dev 1024 Feb 6 12:34 config.txt
drwxr-xr-x 2 ubuntu dev 4096 Feb 6 12:35 logs
-rwxr-xr-x 1 ubuntu www 12288 Feb 6 12:36 deploy.sh
Breaking down each column:
- First character:
-is file,dis directory. - Next 9 characters:
rwxin chunks of 3 (Owner / Group / Others). - Number: Hard link count (usually not important).
- Two names:
ubuntu dev- owner and group. - Byte size: File size.
- Date/Time: Last modified timestamp.
- File name.
config.txt has rw-r--r-- (644):
- Owner (ubuntu): Read, write.
- Group (dev): Read only.
- Others: Read only.
logs directory has rwxr-xr-x (755):
- Owner: Read, write, enter.
- Group: Read, enter (can't create files).
- Others: Read, enter.
deploy.sh script has rwxr-xr-x (755):
- Owner: Read, modify, execute.
- Group (www): Read, execute only.
- Others: Read, execute only.
chmod: Changing permissions
Numeric method (absolute permissions)
Most straightforward approach.
$ chmod 755 deploy.sh
# Owner: rwx(7), Group: r-x(5), Others: r-x(5)
$ chmod 644 config.txt
# Owner: rw-(6), Group: r--(4), Others: r--(4)
$ chmod 600 ~/.ssh/id_rsa
# SSH private key should only be readable/writable by owner
# Group and Others: ---(0)
If SSH key permissions are wrong, SSH will refuse with "permissions are too open" error. Actually running chmod 644 ~/.ssh/id_rsa causes ssh to throw "UNPROTECTED PRIVATE KEY FILE!" errors.
Symbolic method (relative permissions)
Use when you want to modify only part of existing permissions.
$ chmod u+x script.sh
# Add eXecute permission for User (owner)
$ chmod g-w config.txt
# Remove Write permission from Group
$ chmod o-rwx secret.txt
# Remove all permissions from Others
$ chmod a+r readme.txt
# Add Read permission for All (owner+group+others)
-
u= User (Owner) -
g= Group -
o= Others -
a= All -
+= Add permission -
-= Remove permission -
== Set permission (ignoring existing)
For example:
$ chmod u=rwx,g=rx,o=r script.sh
# Owner: rwx, Group: r-x, Others: r--
# Numerically equivalent to 754
Recursive permission changes
To apply to all files/directories inside a folder, use -R option.
$ chmod -R 755 /var/www/html
# Set entire web server directory to 755
Warning: -R is powerful and dangerous. Misuse can break system file permissions.
chown: Changing ownership
Basic usage
$ chown ubuntu config.txt
# Change owner of config.txt to ubuntu
$ chown ubuntu:dev config.txt
# Owner becomes ubuntu, Group becomes dev
$ chown :www deploy.sh
# Change only Group to www (owner unchanged)
Real scenario: Web server permissions
Nginx or Apache web servers typically run as the www-data user.
$ sudo chown -R www-data:www-data /var/www/html
$ sudo chmod -R 755 /var/www/html
This ensures:
- Web server can read and execute files.
- Outsiders can only read (no write), keeping it secure.
If the web server needs to write log files:
$ sudo chown www-data:www-data /var/log/nginx
$ sudo chmod 755 /var/log/nginx
chgrp: Changing group only
Instead of chown :group, you can use chgrp.
$ chgrp dev config.txt
# Change group of config.txt to dev
In practice, chown :dev config.txt is more commonly used.
Docker volume permission issues: My actual experience
Files created inside a Docker container sometimes become inaccessible from the host.
$ docker run -v $(pwd)/data:/app/data myapp
# Creates /app/data/output.log inside container
The container runs as root, but on the host I'm the ubuntu user, causing a permission conflict.
Solution:
# In Dockerfile, specify user
RUN useradd -m -u 1000 appuser
USER appuser
Or from the host:
$ sudo chown -R $(whoami):$(whoami) ./data
After several rounds of this struggle, the concept "container and host UID/GID must match" finally clicked.
Special permissions: SUID, SGID, Sticky Bit
SUID (Set User ID)
When a file is executed, it runs with the owner's permissions.
$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 /usr/bin/passwd
See that s? That's SUID.
The passwd command needs to modify /etc/shadow, which only root can write. Thanks to SUID, when a regular user runs passwd, it executes with root privileges, allowing password changes.
$ chmod u+s script.sh
# Or
$ chmod 4755 script.sh
Prepend 4 to enable SUID.
SGID (Set Group ID)
When a file is executed, it runs with the group's permissions. When applied to a directory, all files created inside inherit the directory's group.
$ chmod g+s shared_folder
# Or
$ chmod 2755 shared_folder
Prepend 2 to enable SGID.
For example, a team project folder:
$ mkdir /project
$ chgrp dev /project
$ chmod 2775 /project
Now, no matter who creates a file inside /project, its group automatically becomes dev.
Sticky Bit
When set on a directory, only the file owner can delete files inside.
The /tmp directory is a classic example.
$ ls -ld /tmp
drwxrwxrwt 10 root root 4096 /tmp
See that t at the end? That's the Sticky bit.
Anyone can create files in /tmp, but you can't delete someone else's files.
$ chmod +t shared_folder
# Or
$ chmod 1777 shared_folder
Prepend 1 to enable Sticky bit.
umask: Default permission settings
When you create a new file, it has automatically applied permissions. That's umask.
$ umask
0022
umask works by "subtraction."
- Default file permissions:
666(rw-rw-rw-) - Default directory permissions:
777(rwxrwxrwx)
umask 0022 means:
- File: 666 - 022 = 644 (rw-r--r--)
- Directory: 777 - 022 = 755 (rwxr-xr-x)
Let's verify:
$ umask 0022
$ touch newfile.txt
$ mkdir newfolder
$ ls -l
-rw-r--r-- 1 ubuntu dev 0 newfile.txt
drwxr-xr-x 2 ubuntu dev 4096 newfolder
Exactly 644 and 755.
If you change to umask 0077:
$ umask 0077
$ touch private.txt
$ ls -l private.txt
-rw------- 1 ubuntu dev 0 private.txt
Now only the owner can read and write.
umask is typically set in ~/.bashrc or ~/.zshrc.
# ~/.bashrc
umask 0022
Practical example: SSH key permissions
SSH private keys must have 600 permissions.
$ chmod 600 ~/.ssh/id_rsa
$ ls -l ~/.ssh/id_rsa
-rw------- 1 ubuntu ubuntu 1679 ~/.ssh/id_rsa
If permissions are wrong:
$ chmod 644 ~/.ssh/id_rsa
$ ssh user@server
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: UNPROTECTED PRIVATE KEY FILE! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/ubuntu/.ssh/id_rsa' are too open.
SSH enforces "private keys must not be exposed to others," so it outright rejects loose permissions.
Public keys can be 644.
$ chmod 644 ~/.ssh/id_rsa.pub
Practical example: Web server permission setup
Suppose you're running an Nginx web server.
$ sudo chown -R www-data:www-data /var/www/html
$ sudo find /var/www/html -type f -exec chmod 644 {} \;
$ sudo find /var/www/html -type d -exec chmod 755 {} \;
This ensures:
- Files:
644(owner can write, others read-only) - Directories:
755(owner can write, others read+enter)
If the web server needs to write to an uploads folder:
$ sudo chmod 775 /var/www/html/uploads
$ sudo chown www-data:www-data /var/www/html/uploads
Now the web server can write files to the uploads directory.
Why chmod 777 is truly dangerous
777 means "Anyone can read, write, delete, and execute freely."
For example, if a web server config file is 777:
$ chmod 777 /etc/nginx/nginx.conf
This means:
- A hacker who gains SSH access can modify
nginx.confand inject malicious code. - With
xpermission, they can even execute scripts.
I once got frustrated with Permission denied errors in a Docker container and lazily ran chmod -R 777 /app.
Security Team found it during log analysis and interrogated "Who did this?" It was embarrassing.
Stick to:
- Files:
644(owner can write) - Executables:
755(owner can write, others can execute) - Sensitive files (SSH keys, password files):
600(owner read/write only)
These are the best practices.
Key concepts I internalized
- rwx is a 3-bit flag system. r=4, w=2, x=1. Add them to get 0-7.
- Owner / Group / Others 3-tier management. The apartment building analogy is perfect.
- x permission on directories means "entry permission." Without it,
cdwon't work. - chmod changes permissions, chown changes ownership. Access badge vs. title deed.
- 777 is the beginning of disaster. Convenient now, regrettable later.
Once these concepts solidified, whenever I see Permission denied, I can immediately think "Check file permissions" or "Verify container UID matches" and respond accordingly.