Standard Linux permissions give you exactly one owner, one group and “everyone else”. Real setups often need more: the deploy user owns the files, Nginx must read them, PHP must write to uploads, and a second developer needs access too. Access Control Lists (ACLs) let you add permissions for any number of extra users and groups, without changing ownership.

Do you have ACL support?

ext4, XFS and Btrfs support ACLs by default on modern distributions. Install the tools if needed:

sudo apt install acl        # Debian/Ubuntu
sudo dnf install acl        # RHEL family (usually preinstalled)

View ACLs: getfacl

getfacl /var/www/example.com
# file: var/www/example.com
# owner: deploy
# group: deploy
user::rwx
user:www-data:r-x
group::r-x
mask::r-x
other::---

In ls -l, a + after the permission string shows that a file has ACLs:

drwxr-x---+ 5 deploy deploy 4096 Oct 14 09:12 example.com

Grant access: setfacl -m

sudo setfacl -m u:www-data:rx /var/www/example.com           # user
sudo setfacl -m g:developers:rwx /srv/project                 # group
sudo setfacl -R -m u:www-data:rX /var/www/example.com         # recursive
  • -m modifies (adds or changes) entries.
  • u:name:perms is for a user and g:name:perms for a group.
  • Capital X adds execute only to directories, and to files that are already executable. It’s the right choice for recursive read access, since it lets the user enter directories without making every file executable.

Default ACLs: apply to new files automatically

A normal ACL affects only existing files. A default ACL on a directory is inherited by everything created inside it later:

sudo setfacl -R -d -m u:www-data:rX /var/www/example.com
sudo setfacl -R -d -m g:developers:rwX /srv/project

Usually you set both: -m for what exists now and -d -m for the future. This solves the classic “new files created by the deploy user aren’t readable by the web server” problem, regardless of anyone’s umask.

Remove ACLs

sudo setfacl -x u:www-data /var/www/example.com     # remove one entry
sudo setfacl -k /srv/project                        # remove the default ACLs
sudo setfacl -b /srv/project                        # remove all ACLs
sudo setfacl -R -b /srv/project                     # recursively

The mask

The mask:: entry is an upper limit for all named users, named groups and the owning group. If the mask is r-x, a u:alice:rwx entry effectively gives only r-x. getfacl shows this as #effective:r-x.

A chmod g-w on a file with ACLs changes the mask, not the owning group’s entry. That can quietly reduce ACL permissions. If ACL access suddenly stops working after a chmod, check the mask:

sudo setfacl -m m::rwx file

Real-world example: web root with a deploy user

Goal: deploy owns and writes the code, Nginx and PHP-FPM (www-data) read everything and write only storage/ and uploads/, and nobody else has access.

SITE=/var/www/example.com
sudo chown -R deploy:deploy "$SITE"
sudo chmod -R o-rwx "$SITE"

# read access for the web server, now and for future files
sudo setfacl -R -m u:www-data:rX "$SITE"
sudo setfacl -R -d -m u:www-data:rX "$SITE"

# write access only where needed
for d in storage public/uploads; do
  sudo setfacl -R -m u:www-data:rwX "$SITE/$d"
  sudo setfacl -R -d -m u:www-data:rwX "$SITE/$d"
done

# the deploy user can always manage files the web server creates
sudo setfacl -R -d -m u:deploy:rwX "$SITE"

The web server also needs x on every parent directory (/var, /var/www) to reach the site. See Linux file permissions.

Back up and restore ACLs

Many tools drop ACLs unless told otherwise:

  • cp -a preserves them, and so does rsync -aAX (-A = ACLs, -X = extended attributes).
  • tar --acls --xattrs -cpf … preserves them in tar archives.

Save and restore the ACLs of a whole tree:

getfacl -R /srv/project > project-acls.txt
setfacl --restore=project-acls.txt

When ACLs are the wrong tool

  • If one group is enough, use plain groups and setgid. It’s simpler to understand and audit. See Linux groups and setgid.
  • Containers and bind mounts: ACLs are matched by UID and GID numbers inside the container.
  • NFS needs ACL support on both sides (NFSv4 has its own ACL model).

Quick reference

Task Command
Show ACLs getfacl path
Add user access setfacl -m u:user:rX path
Add group access setfacl -m g:group:rwX path
Recursive setfacl -R -m …
Default for new files setfacl -d -m …
Remove an entry setfacl -x u:user path
Remove all ACLs setfacl -b path
Back up ACLs getfacl -R dir > acls.txt
Copy with ACLs rsync -aAX

Related: chmod, chown and umask.

  • Linux
  • Permissions
  • Security