File Permission System
Linux assigns permissions across three layers for every file and directory: owner, group, and others. Each layer has three permissions — read (r), write (w), and execute (x) — controlling access with a total of 9 bits.
You can check file permissions with the ls -l command.
ls -la /opt/my-app/
# drwxr-xr-x 5 deploy deploy 4096 Feb 28 10:00 .
# -rw-r--r-- 1 deploy deploy 1234 Feb 28 10:00 config.yaml
# -rwxr-x--- 1 deploy deploy 5678 Feb 28 10:00 start.sh
# -rw------- 1 deploy deploy 256 Feb 28 10:00 .env
# Permission string breakdown:
# -rwxr-x---
# │└┬┘└┬┘└┬┘
# │ │ │ └── others: --- (no permissions)
# │ │ └───── group: r-x (read + execute)
# │ └──────── owner: rwx (read + write + execute)
# └────────── type: - (regular file), d (directory), l (symbolic link)
The numeric (octal) representation of permissions is also commonly used.
| Permission | Character | Number | Meaning |
|---|---|---|---|
| Read | r | 4 | Read file contents / list directory |
| Write | w | 2 | Modify file / create or delete files in directory |
| Execute | x | 1 | Execute file / enter directory (cd) |
For example, 755 means owner (7=4+2+1=rwx), group (5=4+1=r-x), others (5=4+1=r-x).
chmod — Changing Permissions
Use the chmod command to change file permissions. There are two methods: numeric and symbolic.
# === Numeric method ===
# 644: owner read+write, group/others read-only
chmod 644 config.yaml
# 755: owner all, group/others read+execute
chmod 755 start.sh
# 600: owner read+write only (ideal for secret files)
chmod 600 .env
# === Symbolic method ===
# u=owner, g=group, o=others, a=all
# + add, - remove, = set
# Add execute permission for owner
chmod u+x deploy.sh
# Remove read permission for others
chmod o-r config.yaml
# Set read+write for group (replaces existing permissions)
chmod g=rw shared-doc.txt
# Grant read permission to everyone
chmod a+r README.md
# === Recursive application to directories ===
# -R: apply to all files/directories below
chmod -R 755 /opt/my-app/public/
# Apply different permissions to files and directories (using find)
# Directories: 755 (x is required for cd entry)
find /opt/my-app -type d -exec chmod 755 {} \;
# Files: 644 (no execution needed)
find /opt/my-app -type f -exec chmod 644 {} \;
Directories require x permission for cd entry. If a directory has x without r, files are only accessible when you know their exact names.
chown — Changing Ownership
Use the chown command to change file owner and group.
# Change owner
sudo chown deploy config.yaml
# Change owner and group simultaneously
sudo chown deploy:deploy config.yaml
# Change group only (same as chgrp)
sudo chown :webteam shared-doc.txt
# Recursive directory change
sudo chown -R deploy:deploy /opt/my-app/
# Change ownership of symbolic link itself (-h option)
sudo chown -h deploy:deploy /opt/my-app/current
Recommended ownership settings for deployment directories.
# Application directory: owned by deploy user
sudo chown -R deploy:deploy /opt/my-app/
# Log directory: owned by app user, group read allowed
sudo chown -R deploy:adm /var/log/my-app/
sudo chmod -R 750 /var/log/my-app/
# Config file: owned by root, app user read-only
sudo chown root:deploy /opt/my-app/.env
sudo chmod 640 /opt/my-app/.env
User and Group Management
Commands for creating and managing users and groups.
# === User management ===
# Create user (-m: create home directory, -s: default shell)
sudo useradd -m -s /bin/bash deploy
# Set password
sudo passwd deploy
# Modify user info (change shell)
sudo usermod -s /bin/zsh deploy
# Add user to group (-aG: append to group while keeping existing groups)
sudo usermod -aG docker deploy
sudo usermod -aG www-data deploy
# Delete user (-r: also delete home directory)
sudo userdel -r olduser
# Check user info
id deploy
# uid=1001(deploy) gid=1001(deploy) groups=1001(deploy),999(docker),33(www-data)
# === Group management ===
# Create group
sudo groupadd webteam
# Add users to group
sudo usermod -aG webteam alice
sudo usermod -aG webteam bob
# Delete group
sudo groupdel oldgroup
# Check groups for current user
groups deploy
# deploy : deploy docker www-data webteam
If you omit the -a option in usermod -aG, all existing groups are removed and only the specified group remains. Always use -aG together.
Special Permissions — setuid, setgid, sticky bit
There are 3 special permissions beyond regular rwx.
| Special Permission | Number | Effect |
|---|---|---|
| setuid (s) | 4000 | File runs with owner’s permissions |
| setgid (s) | 2000 | File: runs with group permissions / Directory: child files inherit parent group |
| sticky bit (t) | 1000 | Only file owner can delete files in the directory |
# setgid directory — useful for team shared folders
# Files created inside automatically belong to webteam group
sudo mkdir /opt/shared
sudo chown root:webteam /opt/shared
sudo chmod 2775 /opt/shared
# drwxrwsr-x 2 root webteam 4096 Feb 28 10:00 /opt/shared
# sticky bit — applied to /tmp by default
ls -ld /tmp
# drwxrwxrwt 10 root root 4096 Feb 28 10:00 /tmp
# The trailing t is the sticky bit (cannot delete other users' files)
sudoers Configuration
Configuration of the /etc/sudoers file that controls sudo command permissions. Never edit it directly — always use visudo.
# Edit sudoers (includes syntax checking)
sudo visudo
# Or add files to /etc/sudoers.d/ directory (recommended)
sudo visudo -f /etc/sudoers.d/deploy
sudoers file format:
# /etc/sudoers.d/deploy
# Format: user host=(run-as-user) command
# Grant full sudo privileges to deploy user
deploy ALL=(ALL) ALL
# Use sudo without password (for automation servers)
deploy ALL=(ALL) NOPASSWD: ALL
# Allow only specific commands (enhanced security)
deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart my-app, /bin/systemctl status my-app
# Group-level settings (%groupname)
%webteam ALL=(ALL) NOPASSWD: /usr/bin/docker, /usr/bin/docker-compose
# Check sudoers file permissions (must be 440)
ls -l /etc/sudoers
# -r--r----- 1 root root 755 Feb 28 10:00 /etc/sudoers
# Verify sudo permissions
sudo -l -U deploy
# User deploy may run the following commands:
# (ALL) NOPASSWD: /bin/systemctl restart my-app
# (ALL) NOPASSWD: /bin/systemctl status my-app
Practical Tips
- Principle of Least Privilege: Grant only the minimum necessary permissions to files. In particular, set
.envand certificate key files to600(owner read+write only). - Never use 777:
chmod 777grants all permissions to all users. This is an immediate red flag in security audits. When problems arise, identify the exact cause and grant only the necessary permissions. - umask settings: Determines default permissions when creating new files. With
umask 022(default), files are created as 644 and directories as 755. On security-critical servers, useumask 027to block access from others. - ACL (Access Control List): When basic rwx is insufficient,
setfacl/getfaclcommands enable fine-grained permission control. For example, you can grant read-only access to a specific user. - Use the sudoers.d directory: Instead of editing
/etc/sudoersdirectly, manage settings as individual files in/etc/sudoers.d/. This prevents configuration conflicts and makes integration with automation tools (Ansible, etc.) easier.