Linux File Permissions and User Management — chmod, chown, sudoers

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.

PermissionCharacterNumberMeaning
Readr4Read file contents / list directory
Writew2Modify file / create or delete files in directory
Executex1Execute 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 PermissionNumberEffect
setuid (s)4000File runs with owner’s permissions
setgid (s)2000File: runs with group permissions / Directory: child files inherit parent group
sticky bit (t)1000Only 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 .env and certificate key files to 600 (owner read+write only).
  • Never use 777: chmod 777 grants 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, use umask 027 to block access from others.
  • ACL (Access Control List): When basic rwx is insufficient, setfacl/getfacl commands 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/sudoers directly, manage settings as individual files in /etc/sudoers.d/. This prevents configuration conflicts and makes integration with automation tools (Ansible, etc.) easier.

Was this article helpful?