Linux File Permissions Explained: chmod and chown Tutorial

Introduction

Working with Linux permissions is a day-to-day task for anyone managing servers, deployments, or multi-user systems. In one concrete example, while administering a LAMP stack for a client web app, I used chmod -R 755 /var/www/html to ensure Apache (running as www-data) could serve files while preventing unauthorized writes to configuration files such as /etc/apache2/apache2.conf. The problem before the change was that some files had been copied with permissive modes allowing group writes; the web server served pages but configuration files were exposed to accidental edits by non-admin users.

Mastering permission tools like chmod and chown — and related concepts such as umask, the sticky bit, SUID/SGID, and ACLs — is fundamental for robust Linux system administration and security. This tutorial shows explicit, actionable examples you can apply on common distributions (Debian/Ubuntu, RHEL/CentOS, Fedora) and provides troubleshooting and security practices for real-world environments.

About the Author

Elena Rodriguez

Elena Rodriguez is a UI/UX Developer & Design Systems Specialist with 10 years of experience creating intuitive user interfaces and scalable design systems. Her expertise spans computer architecture, web programming, operating systems, and security considerations in UI development. Elena has worked on enterprise-level applications, focusing on accessibility, responsive design, and creating consistent user experiences across multiple platforms and devices.

Using chmod to Change File Permissions

Understanding chmod Syntax

The chmod command modifies file mode bits. You can use symbolic notation or octal notation:

  • Symbolic: u (user/owner), g (group), o (others), a (all). Example: chmod u+x filename adds execute permission for the owner without changing other bits.
  • Octal: read=4, write=2, execute=1. Combine them per class: owner+group+others → three digits. Example: chmod 755 filename sets owner=7 (rwx), group=5 (r-x), others=5 (r-x).

This command gives the user execute permission on the script.


chmod u+x myscript.sh

Now, the user can run myscript.sh without errors.

Changing Permissions Recursively

Recursive changes are common when deploying web roots or application directories. The -R flag walks the tree and adjusts each entry. Example for a web root on Debian/Ubuntu running Apache:


chmod -R 755 /var/www/html

Use recursion carefully. If configuration files under /etc or deployment secrets are present in the tree, a blanket -R may expose them. A safer approach is to set directory and file modes separately:


# Directories: readable and searchable
find /var/www/html -type d -exec chmod 755 {} +
# Files: readable but not executable by default
find /var/www/html -type f -exec chmod 644 {} +

This pattern avoids inadvertently making all files executable and preserves the intended rights for directories vs. files.

Interpreting ls -l Output

Use ls -l to verify current permissions, owner, group, and special bits. Example output explained line-by-line:


-rwsr-sr-x 1 root staff 12345 Jan  1 12:00 /usr/local/bin/someprog
  • First char ("-", "d", "l"): file, directory, or symlink.
  • Next nine chars: three groups of three bits (owner/group/others). Here rws for owner indicates read, write, and SUID (s) set in place of x.
  • Numbers: link count, owner (root), group (staff), size, timestamp, and name.

To convert symbolic to octal manually, map each triad: rwx=7, r-x=5, rw-=6. Special bits form the leading (fourth) octal digit: SUID adds 4, SGID adds 2, sticky adds 1. Combine those to get the leading digit.

Example conversion (step-by-step) for the line above:

  1. Break the permission string into components: - r w s r - s r - x → owner triad: r w s, group triad: r w s (the s here indicates SGID plus execute), others triad: r - x.
  2. Map each triad to octal: owner rwx7, group rwx7, others r-x5.
  3. Determine special leading digit: SUID is present → 4; SGID is present → +2; sticky not shown → +0. In this example SUID set gives the leading digit 4.
  4. Combine: leading special digit + owner + group + others → 4 7 7 54775.

You can then apply this octal directly with chmod 4775 /usr/local/bin/someprog (use caution with SUID/SGID programs).

umask and Default Permissions

umask sets the default permission mask applied when new files and directories are created. It subtracts bits from the process default (files start as 666, directories as 777 before applying umask).

Common umask values and effects:

  • umask 022 → new files 644, dirs 755 (typical for shared web servers where group/others read)
  • umask 002 → new files 664, dirs 775 (used in collaborative environments where group write is allowed)
  • umask 077 → new files 600, dirs 700 (restrictive, user-only access)

Explicit calculation example: for files (default 666) and umask 022, compute 666 - 022 = 644. For directories (default 777) with the same umask, compute 777 - 022 = 755. This subtraction is bitwise on the permission bits and is a helpful mental model for debugging default permissions.

Set umask per user in their shell startup (e.g., ~/.bashrc) or globally in /etc/profile. Example:


# set a collaborative default where group members can write
umask 002

When troubleshooting unexpected file modes, check the process' effective umask (run umask in the shell or inspect systemd unit files that may override it with UMask= in the service file).

Using chown to Change File Ownership

Understanding the chown Command

chown changes file owner and group. Ownership determines which user and group class the file's permission bits apply to. Use it when services or users need specific ownership to operate correctly.

Examples with concrete contexts:

  • Assign a project directory to the deploy user and developers group used by your CI/CD pipeline:

    
    sudo chown -R deploy:developers /srv/myapp
    
  • Systemd service files or system-level daemons often require specific owners — for example, ensure a system service reading files under /opt/service is owned by the service account used in the unit file.

When restoring backups copied as root, fix ownership to the intended user rather than leaving everything owned by root:


sudo chown -R alice:alice /home/alice/restore

Advanced: SUID, SGID and Sticky Bit

Special permission bits change runtime behavior:

  • SUID (setuid): When set on an executable, the program runs with the file owner's privileges. Represented as an s in the owner's exec slot. To set: chmod u+s /path/to/bin or numeric chmod 4755 /path/to/bin. Use sparingly; SUID root programs are frequent attack targets. For more on capability-based alternatives, consult your system man pages (e.g., man capabilities) and prefer Linux capabilities where appropriate.
  • SGID (setgid): On executables, runs with the group owner privileges. On directories, it forces new files/dirs to inherit the directory's group. Set with chmod g+s /some/dir or numeric chmod 2755 /some/dir.
  • Sticky bit: On directories, restricts deletion so only owner, directory owner, or root can remove files. Common on /tmp. Set with chmod +t /tmp or chmod 1777 /tmp.

Examples:


# Make a binary setuid to root (USE WITH CAUTION)
sudo chmod 4755 /usr/local/bin/someprog

# Ensure files in a shared group directory inherit the group
sudo chmod 2775 /srv/shared

# Set sticky on a public temp directory
sudo chmod 1777 /srv/public_tmp

Security tip: Audit SUID/SGID binaries on systems regularly using find:


# Find SUID files
find / -perm /4000 -type f 2>/dev/null
# Find SGID files
find / -perm /2000 -type f 2>/dev/null

When possible, reduce attack surface by removing unnecessary SUID bits and using capabilities (see man capabilities) instead of SUID root for fine-grained privilege allocation.

Using ACLs for Fine-Grained Control

POSIX ACLs (Access Control Lists) allow permissions beyond the simple owner/group/others model. Common tools are setfacl and getfacl, provided by the acl package available on major distributions (Debian, Ubuntu, RHEL, CentOS, Fedora). For details, consult the local manual pages (for example, run man setfacl and man getfacl on your system).

Examples:


# Give user 'bob' read+execute on a directory without changing group/owner
sudo setfacl -m u:bob:rx /srv/myproject

# Recursively give group 'qa' read-only access
sudo setfacl -R -m g:qa:rX /srv/myproject

# View ACLs
getfacl /srv/myproject

ACLs are especially useful when multiple users from different groups need specific rights. Keep documentation of ACLs as part of deployment notes because they are not visible in standard ls -l output (though a trailing + on ls -l indicates ACLs exist).

Practical Examples and Use Cases

Real-World Scenarios for Using chown

Examples with explicit context:

  • Web server config: Ensure Nginx can read site configs by assigning them to the web user and group. Example on Debian/Ubuntu:

    
    sudo chown www-data:www-data /etc/nginx/sites-available/default
    	sudo systemctl reload nginx
    
  • CI/CD deployments: A CI runner (user gitlab-runner or jenkins) may require ownership of build artifacts. After copying artifacts, set ownership so the service can operate without sudo.
  • Backup restores: After restoring from a tarball created on another server, fix ownerships using a verified mapping (avoid bulk chown root:root unless intended).

To recursively change ownership for a directory used by an application:


sudo chown -R appuser:appgroup /opt/myapp

Permission Evaluation Flow Diagram

Permission Evaluation Flow Flowchart showing how the kernel evaluates file access: owner, group, others, then ACLs and special bits Process UID/GID Owner Owner bits (r/w/x) Group Group bits (r/w/x) Others Other bits (r/w/x) ACLs / Special Bits SUID / SGID / sticky / ACL
Figure: Kernel permission evaluation order and the role of ACLs and special bits

Troubleshooting & Security Tips

  • When a service can't read a file, check ownership (ls -l), SELinux/AppArmor context (if applicable), and whether ACLs are present (getfacl).
  • If scripts cannot execute, verify the execute bit and interpreter line (shebang), and confirm filesystem mount options — some filesystems may be mounted with noexec.
  • Audit SUID/SGID binaries periodically. Remove SUID from unnecessary binaries; prefer capability-based models (Linux capabilities) instead of SUID root where possible (see man capabilities for reference).
  • Store secrets outside web roots and set restrictive permissions (e.g., chmod 640, owned by the service user).
  • Use find with permission predicates to detect risky files (world-writable dirs, unexpected SUIDs):

    
    # Find world-writable directories not sticky
    find / -type d -perm -0002 ! -perm -1000 -print 2>/dev/null
    

Best Practices for Managing File Permissions

Understanding Permission Management

When standardizing permission policies for a project, define roles, default umask, and deployment ownership. Example policy snippet used on a recent project:

  • Application files: owned by appuser:appgroup, dirs 750, files 640.
  • Static assets served by web server: owned by www-data:www-data, files 644, dirs 755.
  • CI artifacts: owned by CI user with group set to deployers; use SGID on shared directories so new files inherit the deploy group.

Implementing Effective Strategies

Use groups for collaborative access instead of making files world-writable. Example workflow:


# Create group and add users
sudo groupadd developers
sudo usermod -aG developers alice
sudo usermod -aG developers bob

# Make the project directory inherit the group
sudo chown -R alice:developers /srv/project
sudo chmod -R 2775 /srv/project

For environments requiring extra granularity, combine ACLs with well-documented policies and automated checks (CI linting for file modes, or a cron job that reports unexpected changes). Keep references to local man pages (man setfacl, man getfacl) in your runbooks for on-demand details.

Key Takeaways

  • chmod modifies permission bits in symbolic or octal form; use targeted changes and find-based patterns for safe recursion.
  • chown controls ownership; ensure services and deployment agents own the files they need to access.
  • umask determines defaults for newly created files and directories; set it deliberately for collaborative vs. private contexts. Remember: files default 666, dirs default 777, then subtract the umask.
  • Use SUID/SGID and sticky bits judiciously and audit them regularly. Prefer capabilities over SUID where applicable.
  • When more granularity is required, POSIX ACLs (setfacl/getfacl) provide per-user and per-group permissions beyond the three-class model.

Conclusion

Mastering chmod, chown, umask, ACLs, and special permission bits gives you precise control over who can access, modify, or execute files on Linux systems. Apply the patterns and checks outlined here in safe test environments (local VMs or cloud instances) before rolling changes into production.

For further reading, the Linux Documentation Project provides foundational material on files and permissions: http://www.tldp.org/


Published: Dec 04, 2025 | Updated: Jan 09, 2026