Linux File Permissions: Chown Pitfall Breaks Nginx Workers
Nginx workers denied read to /etc/nginx after chown to deploy:deploy with 750 — fix: always check process user's permissions for config files.
- Linux permissions control read, write, execute for three scopes: owner, group, other
- Each scope has three bits, combined into a nine-bit string like rwxr-xr--
- chmod changes permissions (symbolic: chmod u+x file; octal: chmod 755 file)
- chown changes owner and group; chgrp changes group only
- setuid, setgid, and sticky bits are special modes that alter execution behavior
- Most permission-related incidents stem from missing execute on directories or incorrect group ownership
Think of every file on your Linux system as a room in a building. File permissions are the lock-and-key rules that decide who can enter (read), rearrange the furniture (write), or actually use the room as an office (execute). There are three groups of people who might want in: the owner of the room, a team the owner belongs to, and everyone else in the building. Linux lets you set different rules for each group — so your boss can edit the room, your colleagues can only look around, and random strangers can't even peek through the window.
Every production outage story that starts with 'well, someone accidentally deleted...' or 'the web server suddenly couldn't read its own config...' has file permissions somewhere near the root cause. Linux file permissions aren't just a theoretical concept buried in a sysadmin handbook — they're the first line of defence between a running application and chaos. Misconfigure them and you've either locked your own service out of its data, or handed a malicious actor a skeleton key to your entire system.
The permission system exists because Linux was designed from day one as a multi-user operating system. Unlike early personal computers where one person owned everything, Unix-descended systems needed a way to let dozens of users share the same machine without stepping on each other's work — or worse, each other's secrets. The read/write/execute model, combined with user/group/other ownership, solves that problem elegantly with just nine bits of information per file.
By the end of this article you'll be able to read any permission string at a glance, write chmod commands in both symbolic and octal notation without guessing, set up group-based access patterns for real team deployments, understand the dangerous-but-useful setuid/setgid/sticky bits, and diagnose permission-related errors before they become production incidents.
What Are Linux File Permissions?
Linux file permissions are a set of rules that determine who can read, write, or execute a file or directory. Every file and directory is owned by a user and a group. Permissions are defined for three categories: the file owner, the owning group, and everyone else (others). The permissions themselves are three bits: read (r), write (w), and execute (x). For directories, read lets you list contents, write lets you create/delete files inside, and execute lets you enter the directory.
The classic permission string looks like this: -rwxr-xr--. The first character is the file type (- for regular file, d for directory, l for symlink). The next nine characters are three triples: owner, group, others. The example above means the owner can read, write, and execute; group members can read and execute; everyone else can only read.
Chances are you've seen Permission denied when trying to access a file you know exists. That's the permission system doing its job — blocking access that wasn't explicitly granted. Understand these nine bits and you'll stop guessing why your app can't read its config.
- Owner triple: the file's owner can set different restrictions for themselves.
- Group triple: files belong to one group; all group members share the second key.
- Other triple: any user not owner or group member — the public lock.
- A missing execute bit on a directory is like locking the building door: even with read, you can't step inside.
Changing Permissions with chmod (Symbolic and Octal)
chmod is your tool to modify permissions. Two modes: symbolic and octal. Symbolic uses letters: u (owner), g (group), o (others), a (all), combined with + (add), - (remove), = (set exactly). Example: chmod u+x script.sh adds execute for the owner. Octal mode uses a three-digit number (0-7) where each digit represents the sum of r=4, w=2, x=1. So 754 means owner=rwx (7), group=rx (5), others=r (4).
Which should you use? Symbolic is clearer for one-off changes like 'add execute for owner.' Octal is more reliable for scripts because it's unambiguous — you set the exact permissions regardless of the current state. Use octal in deployment pipelines.
A common mistake: chmod 777 to 'fix' a permission problem. Don't. It's lazy and dangerous. Always figure out the minimum permissions needed. For directories, note that execute is required to enter — so web apps often need 755 on public directories.
Changing Ownership with chown and chgrp
chown changes the owner and/or group of a file. chgrp changes only the group. The syntax: chown newowner:newgroup file — if you omit the group, the file's group stays unchanged; if you omit the owner, you must include a colon: :newgroup. Common scenario: after deploying code with a build tool (like Jenkins running as jenkins user), the files end up owned by jenkins:jenkins. But your web server runs as www-data. The server can't read the files. The fix: chown -R www-data:www-data /var/www/app or better, chown -R www-data:appgroup /var/www/app and use group permissions.
Important: only root can change file owners. However, the owner of a file can change its group to any group they belong to (with chgrp). So if an app runs as a non-root user but needs to share files with a team, the user can chgrp the files to a common group.
A tricky edge case: symbolic links. By default, chown and chgrp follow symlinks — they change the target, not the link itself. To change the link's ownership, use chown -h. This catches many admins off guard.
id, getent passwd, getent group. Know your service's runtime user (check systemd service file or process table with ps aux) – that's who needs access.Special Modes: setuid, setgid, and the Sticky Bit
Beyond the basic nine bits, Linux provides three special permission modes that change how executables and directories behave:
- setuid (octal 4000 or u+s): When set on an executable, the process runs with the file owner's privileges, not the user who launched it. Classic example:
/usr/bin/passwd— it's owned by root and has setuid, so normal users can change their own password (which writes to/etc/shadowthat only root can write to). - setgid (octal 2000 or g+s): On executables, the process runs with the group of the file. On directories, new files created inside inherit the directory's group, not the creating user's primary group — incredibly useful for shared team directories.
- Sticky bit (octal 1000 or o+t): On directories, only the owner of a file can delete or rename that file. Most commonly used on
/tmp— everyone can write to /tmp, but only the file owner can remove what they created.
Setuid is powerful and dangerous. A bug in a setuid binary can give an attacker root privileges. That's why Linux ignores setuid on scripts (shebangs) for security reasons — but it still works on compiled binaries. Use setuid sparingly and audit it regularly.
Setgid on directories is a safe and effective pattern for collaborative workspaces. Example: chmod g+s /shared/project; chown :projectteam /shared/project.
Real-World Patterns for Team Access
In production, it's rare to find a machine with only one user. You have deployment users, application users, monitoring agents, and human admins. The trick is to set up permissions so that every process gets exactly what it needs and nothing more.
Pattern 1: The Deploy User + Web Server User - Code is delivered by a deploy user (e.g., jenkins or deploy). The web server runs as www-data. - Best approach: make both users part of a common group like appgroup. Set files to 750 and directories to 755, owned by deploy:appgroup. The deploy user writes, the web server reads.
Pattern 2: Shared Project Directory - Team members need to edit files in /home/projects/foo. Each member has their own login. - Create a group fooproj, add all members. Set ownership nobody:fooproj (or any user, doesn't matter as long as group exists). Use setgid on the directory: chmod g+s /home/projects/foo; chmod 2775 /home/projects/foo. Now every file created inside automatically gets group fooproj and group-write permission.
Pattern 3: Locking Down Sensitive Config - Configuration files for a service (e.g., database credentials) should be readable only by the service user and root. Set chmod 600 or 640 if group needs read. Use chown root:servicegroup. - For log files, use 640 with owner being the service user and group being a monitoring group. Add the monitoring tool's user to that group.
These patterns avoid the 'chmod 777 everything' anti-pattern and keep your system secure.
setfacl -m g:deploy:rwx,g:www-data:rx /path. But be aware: ACLs add complexity and are not visible in 'ls -l' — you need getfacl to see them.Debugging Permission Errors in Practice
When you hit a permission error, don't guess. Follow a systematic approach:
- Identify the process user:
ps aux | grep service-name– that user is who needs access. - Check the file's permissions and ownership:
ls -la /path,stat -c '%a %U %G' /path. - Check the path: A directory in the path may lack execute permission. Use
namei -l /full/path. - Check groups:
id $service_user— is the service user in the group that owns the file? - Check for ACLs:
getfacl /path— ACLs override basic permissions. - Check for mount options:
mount | grep /path— the filesystem may be mounted withnoexecornosuid.
Most permission bugs are simple: missing execute on a directory, wrong group membership, or an accidental chmod 000 from a script. But sometimes the problem is subtle — like a SELinux or AppArmor policy that denies even though file permissions look correct. Always check dmesg or ausearch for AVC denials if you're on an SELinux-enabled system.
The Web Server That Couldn't Read Its Own Config
- Always check which system user actually reads files your service depends on.
- Never chown entire directories without understanding the process ownership.
- Document the required ownership and permissions for every component in your deployment playbook.
Key takeaways
Common mistakes to avoid
5 patternsUsing chmod 777 as a quick fix
Forgetting the execute bit on directories
Changing ownership recursively without understanding the scope
Assuming setuid works on interpreted scripts
Ignoring path permissions when troubleshooting
Interview Questions on This Topic
What does the permission string '-rwsr-xr--' mean? Who can execute the file and with what privileges?
Frequently Asked Questions
That's Linux. Mark it forged?
6 min read · try the examples if you haven't