
September 10, 2026
13 min read
By Kokil Thapa | Last reviewed: September 2026
Linux file permissions and ACLs explained starts with a simple idea: every file and directory carries an owner, a group, and a rule set that decides who can read, write, or execute it. That model works until a team grows, a web app needs shared write access, or a deployment script breaks because storage/ is not writable. On production Ubuntu servers I maintain for Linux system administration clients, permission errors still rank among the top post-deploy failures. This guide walks through classic permission bits, special modes, ACLs, and the commands you actually use when debugging a live box.
chmod, chown, and umask for standard access, then setfacl when multiple accounts need different rights on one path.What Are the Basic Linux File Permission Bits?
Every file on a Linux filesystem stores metadata in an inode. Part of that metadata is the permission mask. You see it as ten characters when you run ls -l. The first character shows the file type. The next nine are permission triplets for the owner, the group, and everyone else.
Each triplet uses three letters: r (read), w (write), and x (execute). A dash means the bit is off. Directories treat execute differently: you need execute on a folder to enter it or list its contents through that path.
$ ls -l /var/www/app/storage/logs/laravel.log
-rw-rw-r-- 1 www-data deploy 4821 Sep 10 09:14 laravel.log Read that line left to right. User www-data owns the file. Group deploy applies to members of that group. Owner bits are rw-. Group bits match. Other bits are r--. Only the owner and group can append log lines. Everyone else can read.
Numeric (octal) notation
Octal notation packs the same nine bits into three digits. Read equals 4. Write equals 2. Execute equals 1. Add them per triplet. Common values:
644— owner read/write, group read, other read (typical config file)755— owner full, group/other read+execute (scripts, directories)600— owner read/write only (private keys,.env)775— shared group write on directories (deploy + web server)
chmod 640 .env
chmod 775 storage bootstrap/cache
chmod 755 artisan On a Laravel web application, those three paths show up in almost every permission checklist. The app binary stays executable. Secrets stay tight. Writable runtime dirs stay group-writable for the deploy user and PHP-FPM pool user.
How Do chmod, chown, and umask Work Together?
Three tools handle most day-to-day permission work. They overlap in purpose but do not replace each other.
chmod— changes permission bits on existing files.chown— changes owner and/or group.umask— subtracts bits from the default mode on new files.
chmod: symbolic and numeric modes
Symbolic mode is readable in scripts. Numeric mode is compact. Both are valid.
chmod u+x deploy.sh
chmod g+w shared/uploads
chmod o-rwx private.key
chmod -R g+s storage The -R flag recurses into directories. Use it carefully on large trees. A wrong recursive chmod on / can brick a server. Scope commands to the project root.
chown and chgrp
Only root (or a process with CAP_FOWNER) can give away ownership. That is intentional. A common pattern after Ubuntu file permissions hardening:
sudo chown -R deploy:www-data /var/www/myapp
sudo find /var/www/myapp -type f -exec chmod 644 {} \;
sudo find /var/www/myapp -type d -exec chmod 755 {} \; chgrp changes only the group. It is shorthand when the owner stays the same.
umask: default mask for new files
When a process creates a file, the kernel applies a default mode, then subtracts the umask. A umask of 022 yields 644 files and 755 directories from typical defaults. Check yours:
umask
umask 002 Setting umask 002 in a shared dev group gives group-writable new files. That helps CI runners and developers co-edit without constant chmod fixes. Document the umask in your deploy notes alongside PHP and Node versions.
Application-level roles differ from filesystem roles. Laravel Spatie Permission role management handles who sees an admin panel. Linux permissions handle whether PHP can write a cache file. Both layers must align.
What Are setuid, setgid, and the Sticky Bit?
Beyond rwx, three special bits change behaviour. They show up in the first octal digit or as s and t in ls -l.
| Bit | On files | On directories | Example use |
|---|---|---|---|
| setuid (4xxx) | Run as file owner | No effect | passwd, legacy tools |
| setgid (2xxx) | Run as file group | New files inherit dir group | Shared storage/ trees |
| sticky (1xxx) | No effect | Only owner deletes own files | /tmp |
The setgid bit on a directory is the one I use most on web servers. When www-data and deploy share group www-data, setgid keeps uploaded files group-owned correctly:
sudo chgrp -R www-data storage bootstrap/cache
sudo chmod -R g+rws storage bootstrap/cache The sticky bit on /tmp stops users from deleting each other's temp files. You rarely set it yourself. The OS already did.
Compare this with stopping Git from tracking file permission changes. Git stores the executable bit, not full Unix modes. A chmod 777 on your laptop may not match production unless you enforce ownership in deploy scripts.
When Do You Need ACLs Instead of Standard Permissions?
Classic permissions allow exactly one owner, one group, and one other rule. That breaks down when you need:
- Two groups with different write levels on one directory
- A single extra user with read-only access to one subtree
- CI, backup, and web-server accounts with distinct rights on the same path
Access Control Lists (ACLs) attach extended entries to a file or directory. POSIX ACLs are the standard on Linux ext4, XFS, and Btrfs when the feature is enabled. Check mount options with mount | grep acl or tune2fs -l /dev/sda1 for ext4.
Reading and writing ACLs
getfacl /var/www/shared/docs
setfacl -m u:backup:rx /var/www/shared/docs
setfacl -m g:developers:rw /var/www/shared/docs
setfacl -d -m g:developers:rw /var/www/shared/docs The -m flag modifies entries. The -d flag sets defaults for new children inside a directory. The -x flag removes an entry. The -b flag strips all ACLs and returns to classic mode only.
After you set an ACL, ls -l shows a plus sign at the end of the mode string:
drwxrwsr-x+ 4 deploy www-data 4096 Sep 10 storage The plus means "more ACL entries exist—run getfacl." That detail saves hours when auditing a server inherited from another host.
ACL vs classic permissions: decision guide
| Scenario | Classic chmod/chown | ACL (setfacl) |
|---|---|---|
| Single web user + deploy group | Usually enough with setgid | Optional |
| Backup user needs read, no group membership | Awkward world-readable dirs | Clean fit |
| Three teams, different folder access | Multiple groups + sudo juggling | Preferred |
| Public read, restricted write | 644/755 works | Overkill |
| Legal document portal with role folders | Hard to maintain | Strong fit |
On legal-tech portals where staff, clients, and automated jobs touch the same upload tree, ACLs reduce the temptation to chmod 777. That shortcut creates real file upload security exposure. Named ACL entries beat world-writable directories every time.
Official references: the chmod manual page documents permission bits, and the setfacl manual page covers ACL entry syntax on current Linux distributions.
How Do You Fix Permission Problems on a Production Web Server?
Permission bugs look like application errors. Laravel throws "failed to open stream" when storage/logs is not writable. Nginx returns 403 when the worker cannot traverse a parent directory. PHP-FPM runs as a pool user—often www-data on Ubuntu—not as your SSH login.
Diagnose before you chmod
namei -l /var/www/app/storage/logs/laravel.log
sudo -u www-data test -w /var/www/app/storage/logs && echo writable
id deploy
groups deploy namei -l prints each path component with permissions. A common failure: every file is correct but /var/www lacks execute for others, so the web user cannot descend the tree. Fix the chain, not just the leaf.
A repeatable Laravel permission recipe
I've used this pattern on production Laravel applications deployed with Deployer 7 and GitLab CI:
- Create a shared group:
www-dataor a project-specific group. - Add deploy and PHP-FPM users to that group.
- Set ownership:
deploy:www-dataon the release tree. - Directories 775 with setgid; files 664.
- Keep
.envat 640 or 600. - Persist
storageandbootstrap/cachein a shared directory across releases.
setfacl -R -m g:www-data:rwX storage bootstrap/cache
setfacl -R -d -m g:www-data:rwX storage bootstrap/cache The capital X in ACL mode adds execute only on directories. That avoids marking every file executable. Document this in your runbook next to automated database backups on Linux and log rotation policies.
Sister sites on shared EC2 infrastructure—legal portals, translation services—share the same deploy pipeline. Wrong ownership after symlink swap is a recurring issue. Reload PHP-FPM after deploy so opcache picks up code, but permissions live on disk independently of opcache.
What not to do
chmod -R 777 is a emergency bandage, not a policy. World-writable paths let any local user alter uploads, inject PHP if execution is misconfigured, and exfiltrate data. If you reach for 777, step back and fix group membership or ACLs instead.
Strong filesystem permissions complement app-level checks. Use a password generator for service accounts. Rotate SSH keys. Restrict sudo. Filesystem ACLs are one layer in a stack that includes firewall rules—see iptables vs nftables—and service isolation via systemd service units.
How Do Default ACLs and ACL Masks Affect Effective Permissions?
Two ACL concepts trip people up: the mask and default ACLs.
The ACL mask
The mask entry caps the maximum permission granted to named users and groups. It does not limit the file owner or the owning group class entry in the same way. If mask is r-- but a named user entry says rw-, effective access is read-only.
getfacl report.pdf
user:alice:rw-
group:finance:r--
mask::r--
other::r-- Alice gets read only because mask wins. Raise mask with setfacl -m m:rw report.pdf when you truly intend group-level write.
Default ACLs on directories
Default entries do not grant access to the directory itself. They define templates for new files and subdirectories created inside. This mirrors setgid behaviour but with per-user granularity.
setfacl -d -m u:ci-runner:rw /var/www/build-artifacts
setfacl -d -m m:rw /var/www/build-artifacts Pair default ACLs with cron jobs that rotate artifacts. Old files keep the mode they were born with. New files pick up current defaults.
For deeper background, the Arch Linux ACL wiki documents POSIX ACL types and chmod interaction. Ubuntu enables ACLs on ext4 by default on current LTS releases.
Monitoring helps catch drift. A nightly script can compare getfacl output to a golden file and alert through your stack—similar to Linux server monitoring with Netdata. On a portfolio project like Adventure Third Pole Trek, booking uploads and supplier documents lived in shared trees where ACL defaults prevented permission regression after manual edits.
Key Takeaways
- Classic Linux permissions use owner, group, and other rwx triplets; octal (
644,775) and symbolic (u+rw,g+r) modes both map to the same bits. - Use
chownfor ownership,chmodfor bits, andumaskfor defaults on newly created files—document all three in deploy runbooks. - setgid on shared directories keeps new files group-owned; sticky bit protects world-writable temp dirs like
/tmp. - ACLs via
setfacladd named users and groups when one owner and one group are not enough—avoidchmod 777. - Debug with
namei -landsudo -u www-data test -wbefore changing production modes. - Align filesystem permissions with app-level auth; they solve different problems and both matter on live servers.
People Also Ask
What is the difference between chmod and setfacl?
chmod changes the classic owner, group, and other bits that every file has. setfacl adds extended entries for specific users and groups without reshuffling ownership. Use chmod for standard web deploy layouts. Reach for setfacl when a third account needs distinct access on the same path.
What does chmod 755 mean?
755 means the owner can read, write, and execute (7). Group and others can read and execute (5) but not write. It is the usual mode for directories and executable scripts. Config files with secrets should be tighter—typically 640 or 600.
How do I see ACL permissions on a file?
Run getfacl /path/to/file. If ls -l shows a plus sign after the mode (drwxr-xr-x+), extended ACL entries exist beyond the nine classic bits. Review them before copying trees with cp -a or rsync, because ACLs may need explicit preservation flags.
Why do new files not match the parent directory permissions?
New file mode equals the creation default minus umask—not the parent mode directly. On directories with setgid, new files inherit the parent group. With default ACLs, new entries copy those templates. If results look wrong, check umask, setgid, and default ACL entries together.
Put Linux File Permissions and ACLs to Work on Your Stack
Linux file permissions and ACLs explained only matter when they keep production stable. Start with owner, group, and mode on every deploy. Add setgid shared dirs before you reach for ACLs. Reach for ACLs before you reach for 777. Tie ownership fixes into your CI/CD step the same way you handle Composer installs and PHP-FPM reloads.
If permission errors block a Laravel app, WordPress site, or custom portal on Ubuntu, structured hardening beats one-off chmod commands. See support and maintenance services for ongoing server care, or review Notary Kathmandu and similar deployments where document uploads depend on correct filesystem access. For hosting and DNS alignment with file paths, domain and hosting setup covers the full stack.
Need help auditing permissions on a live server or writing a deploy recipe that survives the next release? Contact us and describe your stack—PHP version, web server, and deploy tool. A short getfacl export often points to the fix within minutes.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

