chmod Command in Linux: Complete Guide to Changing File Permissions and Parameters

chmod command in Linux and it perimeters

chmod was the command that finally made Unix permissions click for me — not when I memorized that 755 means “rwxr-xr-x,” but when I understood why it means that, as three octal digits each built from the same three bits. Once that clicked, symbolic mode (u+x, go-w) stopped being a separate thing to memorize and became an obvious alternate notation for the same underlying bits. This guide covers both notations, the special bits that trip people up (setuid, setgid, sticky), and the real-world patterns I use for securing files and directories.

What Is the chmod Command?

chmod stands for “change mode.” It changes the permission bits of files and directories:

$ chmod --version
chmod (GNU coreutils) 9.4
Copyright (C) 2023 Free Software Foundation, Inc.

Basic Syntax

chmod [OPTION]... MODE[,MODE]... FILE...
chmod [OPTION]... OCTAL-MODE FILE...
chmod [OPTION]... --reference=RFILE FILE...

How Permission Bits Actually Work

Every file’s mode is stored as a set of bits in its inode. The permission-relevant bits break into three triads — owner, group, other — each with three flags: read (r, value 4), write (w, value 2), and execute (x, value 1). Adding those values together per triad gives you the familiar octal digits: 7 = rwx, 6 = rw-, 5 = r-x, 4 = r–, and so on down to 0 = —.

I tested this directly:

$ chmod 755 src.txt
$ ls -l src.txt
-rwxr-xr-x 2 root root 6 Jul 31 01:36 src.txt

7 (owner) = rwx, 5 (group) = r-x, 5 (other) = r-x — exactly matching the string rwxr-xr-x.

For a regular file, read means “can view content,” write means “can modify content,” and execute means “can run it as a program or script.”

For a directory, the same bits mean something different and this is the part that trips people up most: read means “can list the names inside it” (ls), write means “can create/delete/rename entries inside it” (note: this is about the directory’s own listing, not the permissions of files inside it), and execute means “can enter it and access things inside by name” (cd into it, or open a file inside it by path). A directory with r-- but no x lets you see filenames but not actually access or stat any of them — a subtlety that catches people by surprise the first time they hit it.

Symbolic Mode Notation

Alongside octal, chmod accepts symbolic expressions built from:

  • Who: u (user/owner), g (group), o (other), a (all)
  • Operator: + (add), - (remove), = (set exactly)
  • Permission: r, w, x, plus special bits s (setuid/setgid) and t (sticky)

I tested a symbolic change directly:

$ chmod u+x src.txt
$ ls -l src.txt
-rwxr-xr-x 2 root root 6 Jul 31 01:36 src.txt

Symbolic mode’s real strength is that it can add or remove specific bits without needing to know or restate the current full modechmod g+w file adds group write regardless of whatever else was already set, while chmod 664 file requires you to specify the complete mode from scratch.

Special Permission Bits

Beyond the basic rwx triads, three additional bits matter a lot in real administration:

  • setuid (4000, symbolic u+s): on an executable, makes it run with the file owner’s privileges rather than the invoking user’s — the classic example is /usr/bin/passwd, which needs root privileges to edit /etc/shadow even when a normal user runs it.
  • setgid (2000, symbolic g+s): on an executable, runs with the file’s group privileges; on a directory, it makes new files created inside inherit the directory’s group instead of the creating user’s primary group — extremely useful for shared team directories.
  • sticky bit (1000, symbolic +t): on a directory, restricts deletion/renaming of files inside it to their owner (or root), even if the directory itself is world-writable — this is exactly what makes /tmp (mode 1777) safe for every user to share without being able to delete each other’s files.

Full List of Parameters

OptionLong formDescription
-c--changesLike verbose, but report only when a change is actually made
-f--silent, --quietSuppress most error messages
-v--verboseOutput a diagnostic for every file processed
--no-preserve-rootDo not treat / specially (dangerous)
--preserve-rootRefuse to operate recursively on /
--reference=RFILEUse RFILE’s mode instead of specifying MODE
-R--recursiveChange files and directories recursively
--helpDisplay help and exit
--versionOutput version information and exit

Recursive traversal control (used with -R):

OptionDescription
-HIf argument is a symlink to a directory, traverse it
-LTraverse every symlink to a directory encountered
-PNever traverse symlinks (default)

Practical Examples with Output

Setting an exact octal mode:

$ chmod 644 report.txt
$ ls -l report.txt
-rw-r--r-- 1 root root 0 Jul 31 01:36 report.txt

Making a script executable:

$ echo '#!/bin/bash
echo hi' > script.sh
$ chmod +x script.sh
$ ls -l script.sh
-rwxr-xr-x 1 root root 18 Jul 31 01:36 script.sh
$ ./script.sh
hi

Removing all access for others:

$ chmod o-rwx private.txt

Setting different permissions for each category at once, symbolically:

$ chmod u=rwx,g=rx,o= script.sh
$ ls -l script.sh
-rwxr-x--- 1 root root 18 Jul 31 01:36 script.sh

Recursive change across a directory tree:

$ chmod -R 750 /srv/app/data

Setting the setgid bit on a shared team directory:

$ chmod g+s /srv/team_shared
$ ls -ld /srv/team_shared
drwxr-sr-x 2 root root 4096 Jul 31 01:36 /srv/team_shared

The lowercase s in the group execute position confirms setgid is active — new files created inside will inherit the directory’s group.

Setting the sticky bit on a shared, world-writable directory:

$ chmod +t /srv/shared_uploads
$ ls -ld /srv/shared_uploads
drwxrwxrwt 2 root root 4096 Jul 31 01:36 /srv/shared_uploads

Copying mode from a reference file:

$ chmod --reference=template.conf myfile.conf

Common Use Cases

  • Making scripts executable after writing or downloading them
  • Locking down configuration files containing secrets (chmod 600)
  • Securing SSH private keys, which SSH refuses to use if they’re group- or world-readable
  • Setting up collaborative directories with setgid for consistent group inheritance
  • Restricting sensitive log or data directories to a service account only

Shell Scripting and Automation

Securing an SSH key immediately after generation, matching what ssh-keygen expects:

ssh-keygen -t ed25519 -f "$HOME/.ssh/id_ed25519" -N ""
chmod 600 "$HOME/.ssh/id_ed25519"
chmod 644 "$HOME/.ssh/id_ed25519.pub"

Locking down a secrets file as part of a deployment script:

#!/usr/bin/env bash
set -euo pipefail

SECRETS="/etc/myapp/secrets.env"
chmod 600 -- "$SECRETS"
chown appuser:appuser -- "$SECRETS"

Setting up a shared team directory with setgid inheritance:

mkdir -p /srv/team_shared
chown root:devteam /srv/team_shared
chmod 2775 /srv/team_shared

Real-World System Administration Workflows

  • SSH key security: sshd and ssh both refuse to use private keys with overly permissive modes — chmod 600 on the private key and 700 on ~/.ssh itself are non-negotiable.
  • Web server permissions: static files typically need 644 (readable by the web server, not writable), while upload directories the application writes to need careful group ownership plus 775 or narrower, never 777.
  • Cron and systemd service files: many services check the mode of their config or unit files and refuse to run with world-writable configs, as a defense against tampering.
  • Shared development directories: 2775 (setgid + rwxrwxr-x) on a team directory keeps new files consistently group-owned without every developer remembering to chgrp manually.

Comparing chmod to Related Commands

  • chmod vs chown: chmod controls what each category can do; chown controls who falls into the owner/group category in the first place. They’re almost always adjusted together.
  • chmod vs umask: chmod changes an existing file’s mode after creation; umask sets the default mask applied to new files and directories at creation time, before any chmod is applied.
  • chmod vs ACLs (setfacl): chmod‘s three-triad model can only express one owner, one group, and “everyone else.” When you need finer-grained control — like read access for exactly two unrelated users — POSIX ACLs via setfacl/getfacl layer additional rules on top of the base mode chmod sets.

Troubleshooting Common chmod Issues

“chmod: changing permissions: Operation not permitted”: you need to own the file (or be root) to change its mode — ownership, not group membership, is what grants the right to chmod in the first place.

Script won’t execute despite chmod +x: check the shebang line (#!/bin/bash etc.) is correct and the first line of the file, and confirm the filesystem the script lives on wasn’t mounted with the noexec option (common on /tmp in hardened setups).

SSH refuses a key even after chmod 600: check the parent directory permissions too — ~/.ssh itself needs to be 700; SSH validates the whole path, not just the key file.

Directory listing works but files inside can’t be opened: classic case of read permission without execute permission on the directory — you need x on a directory to traverse into it and access anything inside by name, not just r to list names.

setuid bit disappears after copying or editing the file: many tools (including cp without -p, and any editor that rewrites the file rather than modifying it in place) don’t preserve setuid/setgid bits by default, and the kernel actively strips setuid on write by some file managers as a security precaution — reapply explicitly if you genuinely need it.

Performance Considerations

chmod -R on very large trees has the same per-inode syscall overhead as chown -R — one operation per file. For huge trees, find /path -exec chmod MODE {} + is roughly equivalent in performance, batching arguments the same way -R already does internally, so there’s little to gain from switching between them beyond filtering flexibility (find lets you target only files, only directories, or match by name pattern).

Security Implications

Getting chmod wrong is one of the most common ways real systems get compromised. World-writable directories without a sticky bit let any user delete or replace another user’s files. Overly permissive private keys get silently rejected by SSH but might be accepted by less careful tools. Setuid root binaries are a classic privilege-escalation target — every setuid root file on a system is worth auditing periodically with find / -perm -4000 -type f 2>/dev/null. Never use 777 as a lazy fix for a permission error; it almost always means the ownership is wrong, not that every user on the system genuinely needs full access.

Best Practices

  • Use the minimum permission that gets the job done — 600 for secrets, 644 for regular readable files, 755 for executables and traversable directories.
  • Use setgid (g+s) on shared team directories instead of manually fixing group ownership after the fact.
  • Use the sticky bit (+t) on any shared, world-writable directory.
  • Periodically audit for unexpected setuid/setgid binaries with find / -perm -4000 / -perm -2000.
  • Never resort to chmod 777 — fix the underlying ownership or group membership instead.

Compatibility Across Distributions

GNU chmod is identical in behavior across Debian/Ubuntu, RHEL/Fedora, Arch, and openSUSE. BusyBox chmod (Alpine, minimal containers) supports octal and basic symbolic modes plus -R but has a reduced option set, notably around --reference and full traversal control (-H/-L/-P) — check chmod --help in minimal images before relying on those.

Summary

chmod is really just an interface onto a small, elegant bit-field: three triads of read/write/execute, plus setuid, setgid, and sticky as extra flags layered on top. Once the octal-to-triad mapping and the directory-specific meaning of each bit are second nature, chmod stops being a command you look up and becomes a tool you reach for confidently — which matters, because getting permissions wrong (in either direction, too open or too closed) is one of the most common sources of both security incidents and confusing “permission denied” support tickets.

References

  • GNU Coreutils Manual — chmod invocation: https://www.gnu.org/software/coreutils/manual/html_node/chmod-invocation.html
  • Linux man-pages project — chmod(1): https://man7.org/linux/man-pages/man1/chmod.1.html
  • chmod(2) system call and permission bit documentation: https://man7.org/linux/man-pages/man2/chmod.2.html
  • Ubuntu Manpage Repository: https://manpages.ubuntu.com/manpages/noble/en/man1/chmod.1.html
Total
0
Shares

Leave a Reply

Previous Post
cd command in Linux and it perimeters

cd Command in Linux: Complete Guide to Changing Directories and Navigation Parameters

Next Post
chown command in Linux and it perimeters

chown Command in Linux: Complete Guide to Changing File Ownership and Parameters

Related Posts