Master Linux File Permissions: How to Use chmod and chown Effectively
This comprehensive guide explains Linux's permission model, the core concepts of owner, group, and others, demonstrates numeric and symbolic chmod usage, details chown operations, explores special bits, ACLs, common real‑world scenarios, troubleshooting steps, security best practices, and provides scripts for auditing and rollback.
Linux Permission Model
Every file and directory has three sets of permissions: owner (user), group, and others. Each set consists of three bits: read (r = 4), write (w = 2), and execute (x = 1). The combination of the three bits is expressed as an octal value.
Viewing permissions
Use ls -l to display the full mode string. ls -l /var/log/syslog Example output:
-rw-r----- 1 syslog adm 12345 May 29 10:30 /var/log/syslogInterpretation:
Owner: read + write
Group: read only
Others: no access
File type: regular file
Numeric representation
The octal value is the sum of the bits for each class (owner‑group‑others). Common values:
0 = --- (no permissions)
1 = --x (execute only)
2 = -w- (write only)
3 = -wx (write + execute)
4 = r-- (read only)
5 = r-x (read + execute)
6 = rw- (read + write)
7 = rwx (all three)
Directory semantics
r – list filenames
w – create, delete, rename entries
x – enter the directory (cd)
Note: write permission on a directory allows deletion of files inside it regardless of the file’s own mode.
Default permissions and umask
New files are created with mode 666 and new directories with 777. The process umask value is subtracted from these defaults. umask Typical values are 0022 (resulting in 0644 files, 0755 directories) or 0002 (0644 files, 0775 directories).
Temporary change (current shell only): umask 0027 Permanent change: add the umask command to ~/.bashrc or /etc/profile.
chmod – Changing mode
Numeric (octal) form
# rwxr-xr-x
chmod 755 /path/to/file
# rw-r--r--
chmod 644 /path/to/file
# rw-------
chmod 600 /path/to/file
# rwx------ (directory)
chmod 700 /path/to/directoryExplanation: the first digit is for the owner, the second for the group, the third for others.
Symbolic form
# Syntax: chmod [who][op][perm] file
# who: u (owner), g (group), o (others), a (all)
# op: + (add), - (remove), = (set exactly)
# perm: r, w, xCommon examples:
# add execute for owner
chmod u+x script.sh
# remove write for group
chmod g-w file.txt
# set others to read‑only
chmod o=r file.txt
# give everyone read & execute
chmod a+rx program
# owner rwx, group & others rx
chmod u=rwx,go=rx fileCombined operations
# owner & group get rw
chmod ug+rw file.txt
# owner rwx, group rx, others r
chmod u=rwx,g=rx,o=r fileRecursive changes
# whole tree
chmod -R 755 /var/www/html
# only files
find /var/www/html -type f -exec chmod 644 {} \;
# only directories
find /var/www/html -type d -exec chmod 755 {} \;Common numeric values
777 – rwxrwxrwx (dangerous, world‑writable)
755 – rwxr-xr-x (executables, public directories)
750 – rwxr-x--- (private directory, owner & group)
700 – rwx------ (private file)
644 – rw-r--r-- (public files, configs)
600 – rw------- (private files, e.g., secrets)
500 – r-x------ (owner read & execute)
400 – r-------- (read‑only, e.g., password files)
Special permission bits
SUID (4xxx) – program runs with file owner’s UID. Set with chmod u+s file or chmod 4755 file. Typical use: /usr/bin/passwd (needs access to /etc/shadow).
SGID (2xxx) – program runs with file group’s GID; on directories new files inherit the directory’s group. Set with chmod g+s dir or chmod 2755 dir. Typical use: shared project directories.
Sticky Bit (1xxx) – on directories, only the file’s owner may delete/rename it. Set with chmod +t /shared or chmod 1777 /shared. Typical use: /tmp.
chown – Changing owner
Basic usage
# change only owner
chown user /path/to/file
# change owner and group
chown user:group /path/to/file
# change only group
chown :group /path/to/fileRecursive
chown -R user:group /path/to/directoryReference file
chown --reference=/etc/passwd /etc/shadowThis copies the owner and group from /etc/passwd to /etc/shadow.
Common scenarios
Web applications (Nginx + PHP‑FPM)
# same user for both services
chown -R www-data:www-data /var/www/html
# separate users (recommended for production)
chown -R nginx:nginx /var/www/html # web server files
chown -R php-fpm:php-fpm /var/www/html/uploads # upload dir
# ACL alternative
setfacl -R -m u:nginx:rx /var/www/html/uploads
setfacl -R -m u:php-fpm:rw /var/www/html/uploadsDatabase files
# MySQL
chown -R mysql:mysql /var/lib/mysql
chmod -R 700 /var/lib/mysql
# PostgreSQL
chown -R postgres:postgres /var/lib/postgresql
chmod -R 700 /var/lib/postgresqlLog directories
chown -R myapp:adm /var/log/myapp
chmod -R 750 /var/log/myappService configuration
chown -R root:root /etc/nginx
chmod -R 640 /etc/nginx/*.conf
chmod 755 /etc/nginxUser and group management
# create a regular user (auto‑creates same‑named group)
useradd -m -s /bin/bash deploy
# create a system user without home and login shell
useradd -r -s /sbin/nologin nginx
# create a group
groupadd developers
# add user to group
usermod -aG developers alice
# view groups of a user
groups alice
id aliceACL – Access Control Lists
Checking ACL support
# enable ACL on ext4 (if not already)
tune2fs -o acl /dev/sda1
mount -o acl /dev/sda1 /mntViewing ACLs
getfacl /path/to/fileSample output:
# file: file.txt
# owner: root
# group: root
user::rw-
user:www-data:rw-
group::r--
group:developers:rw-
mask::rw-
other::r--Setting ACL entries
# grant user read/write
setfacl -m u:alice:rw /var/www/html/file.txt
# grant group read/execute on a directory
setfacl -m g:dev:rx /var/www/html/dirDefault ACLs (inheritance)
# default ACL for new files in uploads
setfacl -m d:u:www-data:rw /var/www/html/uploads
# verify
getfacl /var/www/html/uploadsRemoving ACL entries
setfacl -x u:alice /path/to/file
setfacl -x g:dev /path/to/file
# remove all ACLs (revert to traditional mode)
setfacl -b /path/to/fileRecursive ACL operations
# apply to all files & sub‑dirs
setfacl -R -m u:www-data:rw /var/www/html/uploads
# set default ACL (affects only newly created items)
setfacl -R -m d:u:www-data:rw /var/www/html/uploadsMask
The effective permission is limited by the mask entry.
setfacl -m m::rwx /path/to/fileWeb‑application ACL example (nginx + php‑fpm)
# directory owned by nginx, readable by nginx, writable by php‑fpm
chown nginx:nginx /var/www/html
chmod 750 /var/www/html
setfacl -R -m u:php-fpm:rw /var/www/html/uploads
setfacl -R -m u:nginx:rx /var/www/html/uploads
# default ACL so new uploads inherit the same rights
setfacl -R -m d:u:php-fpm:rw /var/www/html/uploads
setfacl -R -m d:u:nginx:rx /var/www/html/uploadsProduction‑environment troubleshooting
1 – Web server cannot read files (403)
Steps:
Check Nginx error log: tail -20 /var/log/nginx/error.log Inspect file mode: ls -la /var/www/html/index.html Identify Nginx run‑user: ps aux | grep nginx Typical causes: wrong owner, missing execute on parent directories, SELinux/AppArmor denial.
# fix ownership and permissions
chown nginx:nginx /var/www/html/index.html
chmod 755 /var/www/html # directory execute
chmod 644 /var/www/html/index.html2 – Web server cannot write (uploads, logs)
Steps:
Check PHP‑FPM or application logs.
Inspect upload directory: ls -ld /var/www/html/uploads Confirm PHP‑FPM user: ps aux | grep php-fpm Verify filesystem is writable: df -h /var/www/html/uploads and
mount | grep /var/www # make uploads writable for php‑fpm
chown php-fpm:php-fpm /var/www/html/uploads
chmod 775 /var/www/html/uploads
# SELinux context (if enabled)
chcon -R -t httpd_sys_rw_content_t /var/www/html/uploads
semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/html/uploads(/.*)?"3 – Database service fails to start
Steps:
Inspect DB error log ( /var/log/mysql/error.log or /var/log/postgresql/postgresql-*-main.log).
Check data directory permissions.
Check configuration file permissions.
# MySQL repair
systemctl stop mysql
chown -R mysql:mysql /var/lib/mysql
chmod -R 700 /var/lib/mysql
systemctl start mysql
# PostgreSQL repair
systemctl stop postgresql
chown -R postgres:postgres /var/lib/postgresql
chmod -R 700 /var/lib/postgresql
systemctl start postgresql4 – SSH public‑key login fails
Required permissions:
/home/username 700 or 755
/home/username/.ssh 700
/home/username/.ssh/authorized_keys 600 # fix permissions
chmod 700 /home/username/.ssh
chmod 600 /home/username/.ssh/authorized_keys
chmod 755 /home/username5 – Script execution “Permission denied”
Check that the script is executable and has a proper shebang.
# add execute bit
chmod +x script.sh
# or set explicit mode
chmod 755 script.sh
# verify interpreter is executable
ls -la /bin/bash6 – Shared directory users cannot access each other’s files
Solution: enable SGID on the directory and adjust umask so new files are group‑writable.
# set SGID and group ownership
chmod 2775 /shared
chown :developers /shared
# ensure new files get group write
umask 002 # add to ~/.bashrc or /etc/profile for the relevant usersSecurity best practices
Principle of least privilege
Grant only the permissions required for a task.
# avoid world‑writable directories
chmod -R 777 /var/www/html # NOT recommended
# recommended layout for a web site
find /var/www/html -type d -exec chmod 755 {} \;
find /var/www/html -type f -exec chmod 644 {} \;
chmod 775 /var/www/html/uploads
chown www-data:www-data /var/www/html/uploadsSensitive file protection
# password files
chmod 640 /etc/passwd
chmod 600 /etc/shadow
# SSH private key
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
# database configs
chmod 640 /etc/mysql/my.cnf
chmod 640 /etc/postgresql/*/main/pg_hba.conf
# application secrets
chmod 600 /var/www/html/.envDedicated service users
useradd -r -s /sbin/nologin nginx
useradd -r -s /sbin/nologin php-fpm
useradd -r -s /sbin/nologin mysql
useradd -r -s /sbin/nologin postgres
useradd -m -s /bin/bash myappRegular permission audit
Example audit script:
#!/bin/bash
echo "=== Permission audit report ==="
echo "Time: $(date)"
echo "--- Sensitive files ---"
for f in /etc/passwd /etc/shadow /etc/group; do
perms=$(stat -c "%a" $f)
owner=$(stat -c "%U:%G" $f)
echo "$f: $owner $perms"
done
echo "--- World‑writable (777) files/dirs ---"
find /var/www -perm -007 -type f 2>/dev/null | head -10
find /var/www -perm -007 -type d 2>/dev/null | head -10
echo "--- Suspicious executables in /tmp ---"
find /tmp -perm /111 -type f 2>/dev/null | head -10Directory permission recommendations
/home/user – 755
/home/user/.ssh – 700
/var/www/html – 755
/var/www/html/uploads – 775
/var/log/app – 750
/data/shared – 2775 (SGID)
/tmp – 1777 (Sticky Bit)
/etc/nginx – 750
/var/lib/mysql – 700
Umask recommendations
# system‑wide default (e.g., /etc/profile)
umask 0027
# team‑shared directory (in ~/.bashrc for members of "developers")
if [ "$(id -gn)" = "developers" ]; then
umask 002
fi
# web‑app user
su - www-data -c "umask 002"Special permission deep‑dive
SUID
Allows a program to run with the file owner’s UID. Common example: /bin/ping is setuid root so ordinary users can send ICMP packets.
# list SUID binaries
find /usr -perm /4000 -type f 2>/dev/null
# detailed list
find /usr -perm /4000 -type f -exec ls -la {} \; 2>/dev/null
# exclude standard system binaries
find / -perm -4000 -type f 2>/dev/null | grep -vE "^/(usr|bin|sbin)/"Security risk: any SUID binary can be abused for privilege escalation. Periodic checks are recommended.
#!/bin/bash
KNOWN="/root/.known_suid_$(date +%Y%m)"
if [ ! -f "$KNOWN" ]; then
find / -perm -4000 -type f 2>/dev/null > "$KNOWN"
echo "Created baseline SUID list: $KNOWN"
exit 0
fi
CURRENT=$(mktemp)
find / -perm -4000 -type f 2>/dev/null > "$CURRENT"
NEW=$(comm -13 "$KNOWN" "$CURRENT")
if [ -n "$NEW" ]; then
echo "New SUID files detected:"
echo "$NEW"
else
echo "No new SUID files"
fi
rm -f "$CURRENT"To remove SUID from a binary:
chmod u-s /usr/bin/someprogram
# or reset to normal executable mode
chmod 755 /usr/bin/someprogramSGID
Two uses: setgid on a file (run with file’s group) and setgid on a directory (new files inherit the directory’s group).
# create a shared directory for developers
mkdir /opt/shared
groupadd developers
chown :developers /opt/shared
chmod 2775 /opt/shared # SGID bit set
# add members
usermod -aG developers alice
usermod -aG developers bob
# verify inheritance
su - alice -c "touch /opt/shared/alice.txt"
ls -l /opt/shared/alice.txt # group should be developersWhen multiple groups need write access, combine SGID with ACL:
mkdir /data/project
groupadd dev
groupadd qa
chown :dev /data/project
chmod 2770 /data/project
setfacl -m g:qa:rw /data/project
setfacl -m d:g:qa:rw /data/project
getfacl /data/projectSticky Bit
Used on public directories so users can only delete their own files.
# typical /tmp directory
ls -ld /tmp # drwxrwxrwt … (t = sticky)
# create a custom public dir
mkdir /opt/public
chmod 1777 /opt/public
chown root:root /opt/publicSELinux / AppArmor interaction
SELinux
# status
getenforce
sestatus
# view context of a path
ls -Z /var/www/html
# change context temporarily
chcon -R -t httpd_sys_content_t /var/www/html
# change context permanently
semanage fcontext -a -t httpd_sys_content_t "/var/www/html(/.*)?"
restorecon -R /var/www/htmlAppArmor (Ubuntu)
# show status
aa-status
# view nginx profile
cat /etc/apparmor.d/usr.sbin.nginx
# reload profiles after edit
systemctl reload apparmorSummary
Core concepts
Three permission classes: owner (u), group (g), others (o).
Three bits per class: read = 4, write = 2, execute = 1.
Directory write permission controls creation/deletion of entries, independent of file mode. umask subtracts bits from the default 666/777 to produce the initial mode of new files/dirs.
Special bits – SUID (4xxx), SGID (2xxx), Sticky (1xxx).
ACLs provide fine‑grained per‑user or per‑group permissions beyond the traditional model.
Common command cheat‑sheet
# Change mode
chmod 755 file # numeric
chmod u+x file # symbolic
chmod -R 755 directory # recursive
# Change owner / group
chown user:group file
chown :group file # only group
chown -R user:group dir # recursive
# View permissions
ls -la file
getfacl file # ACL view
# ACL manipulation
setfacl -m u:alice:rw file
setfacl -m g:dev:rx dir
setfacl -m d:u:bob:rw dir # default for new items
setfacl -b file # remove all ACLs
# SELinux
getenforce
chcon -t httpd_sys_rw_content_t /var/www/html/uploads
semanage fcontext -a -t httpd_sys_content_t "/var/www/html(/.*)?"
restorecon -R /var/www/htmlLearning recommendations
Focus on understanding the model rather than memorising numbers.
Experiment in a test environment: change modes, observe access results.
Pay attention to error messages – they often indicate the exact permission problem.
Apply the principle of least privilege to every service.
Record original permissions before changes; use getfacl backups for safe rollback.
Proper permission management is a cornerstone of Linux security and reliability. Consistent use of the techniques above prevents most production‑environment incidents.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaGe Linux Operations
Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
