InfraRunBook
    Back to articles

    Linux File Permissions chmod chown umask Explained

    Linux
    Published: Apr 9, 2026
    Updated: Apr 9, 2026

    A deep-dive into Linux file permissions covering chmod, chown, and umask — including octal notation, special bits, and real-world operational scenarios from a senior infrastructure engineer.

    Linux File Permissions chmod chown umask Explained

    The Unix Permission Model: More Than Just rwxrwxrwx

    Every file and directory on a Linux system has an owner, a group, and a set of permission bits. That's the foundation. But I've seen engineers who've been running Linux systems for years still get bitten by the subtleties — especially around what execute permission means on a directory, or why umask behaves the way it does when you're running a systemd service. Let's break it down properly.

    When you run

    ls -l
    on a file, you get something like this:

    -rwxr-x--- 1 infrarunbook-admin devops 4096 Apr 9 10:22 deploy.sh

    That first column is the permission string. The leading dash means it's a regular file. A

    d
    means directory,
    l
    for symlink,
    b
    for block device. After that come three groups of three bits: owner permissions, group permissions, and "others" — everyone on the system who isn't the owner and isn't in the file's assigned group.

    Read, Write, Execute — What They Actually Mean

    On a file, the semantics are intuitive. Read (

    r
    ) means you can open and view the file's contents. Write (
    w
    ) means you can modify it. Execute (
    x
    ) means you can run it as a program. Simple enough.

    Directories are where engineers get confused. On a directory, the bits mean something entirely different. Read permission lets you list its contents — essentially lets

    ls
    work. Write permission lets you create, delete, or rename files inside it. Execute permission is the one that trips people up: it's what lets you traverse into the directory or access anything inside it by path at all.

    Here's the implication. If you have a file at

    /var/data/reports/q1.csv
    and the
    reports
    directory has permissions
    r--
    for a given user, they can list the directory but can't open any file inside it. They need execute (traverse) permission on every directory component in the path. I've seen this exact misunderstanding break deployments where someone ran
    chmod 444
    on a directory thinking they were making it read-only for content protection, but actually broke all read access to every file inside it. The permission on the directory gatekeeps entry — the permission on the file only applies once you're already inside.

    Octal Notation and Symbolic Notation

    There are two ways to express permissions with chmod: octal (numeric) and symbolic. You'll need both.

    Octal treats each group of three bits as a three-bit binary number. Read is 4, write is 2, execute is 1. You add them together to get the digit for each class. So

    rwx
    is 7,
    r-x
    is 5,
    r--
    is 4, and
    ---
    is 0. A permission string like
    rwxr-x---
    becomes
    750
    in octal — readable once you internalize it, and compact enough for scripts.

    chmod 750 deploy.sh
    chmod 644 /etc/app/solvethenetwork.conf
    chmod 600 /home/infrarunbook-admin/.ssh/id_rsa

    Symbolic notation is more readable when you want to make targeted changes without knowing the current full state. You specify who (

    u
    for user/owner,
    g
    for group,
    o
    for others,
    a
    for all), the operation (
    +
    to add,
    -
    to remove,
    =
    to set exactly), and which bits to affect.

    chmod u+x script.sh           # add execute for owner only
    chmod go-w sensitive.conf     # remove write from group and others
    chmod a=r public.txt          # set everyone to read-only, stripping everything else
    chmod u=rwx,g=rx,o= app.sh   # set all three classes explicitly in one pass

    In my experience, octal is better for setting full permissions from scratch, and symbolic is better for incremental changes. Symbolic has one key operational advantage: it doesn't accidentally wipe permissions you didn't intend to touch, which matters when you're scripting changes across a heterogeneous file tree.

    Recursive chmod and the File vs Directory Problem

    The

    -R
    flag makes chmod recursive, but use it carefully. Running
    chmod -R 755
    on a directory containing both regular files and subdirectories will stamp execute permission onto all files — including data files and configs that have no business being executable. The right approach for mixed content is to target files and directories separately with
    find
    :

    find /var/www/solvethenetwork -type d -exec chmod 755 {} \;
    find /var/www/solvethenetwork -type f -exec chmod 644 {} \;

    This is a pattern I reach for every time I'm setting up a web root. Directories need execute for traversal, regular content files don't need it, and keeping them separate prevents a whole category of security misconfigurations.

    chown and chgrp: Changing Ownership

    While chmod controls what permissions are set,

    chown
    controls who owns the file. Ownership has two components: the user owner and the group owner. You can change both at once with the
    user:group
    syntax, or just change the group with
    chgrp
    .

    chown infrarunbook-admin:devops /opt/app/config.yml
    chown -R infrarunbook-admin:www-data /var/www/solvethenetwork/
    chgrp wheel /usr/local/bin/admin-tool

    Only root can transfer file ownership to a different user. A regular user can change a file's group, but only to a group they're already a member of. This becomes a real stumbling block in deployment scripts running under a service account — the script might have permission to write a file, but can't then reassign ownership to a different service user without escalation. Plan your deployment user's group memberships accordingly.

    A pattern that works well in provisioning: create the directory structure as root, set ownership to the service account before the application ever runs, and let the application write files under its own identity from the start. That avoids a whole class of runtime permission failures.

    umask: The Permission Mask You Probably Forget About

    umask is the sleeper topic here. Most engineers know chmod and chown, but umask is what silently determines the default permissions on every new file and directory created on the system — and it's not always obvious why a file ends up with unexpected permissions.

    The umask is applied against the maximum default permissions at creation time. For files, the theoretical maximum default is

    666
    (rw-rw-rw-) — no execute by default, because executables should always be made executable deliberately. For directories, the maximum is
    777
    . The umask value specifies which bits to mask off (remove) from those maximums.

    A umask of

    022
    means: remove write permission from group and others. So a new file gets
    666 & ~022 = 644
    (rw-r--r--) and a new directory gets
    777 & ~022 = 755
    (rwxr-xr-x). That's the standard for most Linux systems and it's a reasonable default for interactive user sessions.

    # Check current umask
    umask
    
    # Set umask for current shell session
    umask 027
    
    # Verify a newly created file
    touch /tmp/testfile && ls -l /tmp/testfile

    A umask of

    027
    is more appropriate for service accounts and security-conscious environments: new files get
    640
    (rw-r-----) and directories get
    750
    (rwxr-x---). Others get zero access. On a multi-tenant system — say, a shared application server at 10.10.5.20 running several services — you don't want application files world-readable by default. Setting a restrictive umask at the service level is far more reliable than trying to audit and lock down files after the fact.

    Where umask Is Actually Set

    umask can live in several places:

    /etc/profile
    ,
    /etc/bashrc
    , user-level
    ~/.bashrc
    or
    ~/.bash_profile
    , and through PAM via
    /etc/pam.d/
    using the
    pam_umask
    module. The PAM approach is the most reliable for service accounts because it applies regardless of how the session starts — login shell, SSH, su, sudo, it doesn't matter.

    # /etc/pam.d/common-session (Debian/Ubuntu) or /etc/pam.d/system-auth (RHEL/Rocky)
    session optional pam_umask.so umask=027

    For services managed by systemd, set it directly in the unit file. This is the piece a lot of engineers don't know about:

    [Service]
    User=infrarunbook-admin
    Group=devops
    UMask=0027

    That

    UMask
    directive ensures files created by the service have the right permissions from the start, regardless of what the OS default umask is. If you're running a service that creates log files, config files, or data files, set UMask in the unit and you'll never need to chase down permission issues caused by mismatched defaults.

    Special Permission Bits: Setuid, Setgid, and Sticky

    Beyond the standard nine permission bits, there are three special bits that alter execution behavior or directory semantics in important ways. These are setuid, setgid, and the sticky bit — represented as a fourth octal digit when set numerically.

    Setuid (SUID)

    When the setuid bit is set on an executable, the program runs with the effective UID of the file's owner rather than the user who launched it. The textbook example is

    /usr/bin/passwd
    . A regular user needs to modify
    /etc/shadow
    to change their password, but that file is only writable by root. The passwd binary is owned by root with SUID set, so when any user runs it, the process temporarily holds root's effective UID for just that operation.

    ls -l /usr/bin/passwd
    -rwsr-xr-x 1 root root 68208 Mar 14 08:00 /usr/bin/passwd

    Notice the

    s
    where the owner's execute bit would normally appear. That's SUID active with execute also set. An uppercase
    S
    means SUID is set but execute is not — which is almost always a mistake and warrants investigation. SUID on shell scripts is ignored by the kernel entirely as a deliberate security measure. Don't try to make a SUID shell script; it won't work, and for good reason.

    Setgid (SGID)

    On executables, SGID works like SUID but for group identity — the program runs with the group of the file rather than the calling user's primary group. On directories, though, SGID serves a genuinely useful collaboration purpose.

    When SGID is set on a directory, new files created inside inherit the directory's group instead of the creator's primary group. This means a shared project directory will have all files consistently owned by the

    devops
    group regardless of which team member creates them. No more chasing down files that ended up owned by
    jsmith
    's personal group and inaccessible to teammates.

    chmod g+s /opt/shared/project
    ls -ld /opt/shared/project
    drwxrwsr-x 2 infrarunbook-admin devops 4096 Apr 9 11:00 /opt/shared/project

    The

    s
    in the group execute position indicates SGID. This pattern is something I reach for on any shared project directory where multiple users collaborate. Combine it with a sticky bit if you also want to prevent users from deleting each other's files.

    Sticky Bit

    The sticky bit on a directory means only the file's owner (or root) can delete or rename files inside it, even if other users have write permission to the directory itself. The canonical example is

    /tmp
    .

    ls -ld /tmp
    drwxrwxrwt 10 root root 4096 Apr 9 12:00 /tmp

    The

    t
    in the others execute position is the sticky bit. Without it, any user with write access to
    /tmp
    could delete anyone else's files, since deletion is a directory operation, not a file operation. Which brings us neatly to the most common misconception about Linux permissions.

    Real-World Scenarios

    A web application on sw-infrarunbook-01 was failing to write log files. The app ran as

    www-data
    , and the log directory was owned by
    infrarunbook-admin
    with permissions
    755
    . World-readable and world-traversable, but not world-writable. The fix was to align the group ownership and make it group-writable:

    chown infrarunbook-admin:www-data /var/log/solvethenetwork-app/
    chmod 775 /var/log/solvethenetwork-app/

    Another classic: SSH refusing to use a private key at

    /home/infrarunbook-admin/.ssh/id_rsa
    with permissions
    644
    . SSH enforces strict key file permission requirements as a security control — it won't use a private key that's group- or world-readable, and it will tell you so. The fix is always the same:

    chmod 600 /home/infrarunbook-admin/.ssh/id_rsa
    chmod 700 /home/infrarunbook-admin/.ssh/

    For a compliance audit finding on a server at 172.16.50.10 flagging world-writable files, here's the find pattern I use:

    find / -xdev -type f -perm -o+w -not -path "/proc/*" 2>/dev/null
    find / -xdev -type d -perm -o+w -not -path "/proc/*" -not -path "/tmp" -not -path "/var/tmp" 2>/dev/null

    The

    -xdev
    flag keeps the search within the current filesystem, avoiding traversal into mounts. Running this as a periodic check — or wiring it into auditd rules — catches unintended world-writable files before an auditor or an attacker does.

    Common Misconceptions

    The biggest one: people assume root is subject to standard permission checks. Root bypasses all permission checks for reads and writes. A file with permissions

    000
    is fully accessible to root. The only exception is execute — root needs at least one execute bit set on a binary somewhere to run it. This matters when you're trying to "lock down" a file from root — you can't, not with standard permissions. That's what security modules like SELinux or AppArmor are for.

    The deletion misconception is the one that surprises the most people. Deleting a file doesn't require any permission on the file itself. It's a directory operation — you're removing an entry from the parent directory's namespace. To delete a file you need write permission on the containing directory. The file's own mode is irrelevant. This is precisely why

    /tmp
    has the sticky bit; without it,
    w
    on the directory would let anyone delete anyone else's files.

    There's also persistent confusion about umask arithmetic. umask doesn't subtract bits numerically — it's a bitwise AND NOT operation. The umask bits represent permissions to strip. A umask of

    022
    doesn't set permissions to
    022
    ; it removes group-write and other-write from whatever the default maximum would be. Thinking of it as a mask rather than a subtraction makes the behavior much clearer.

    Finally, a misconception about the limits of standard permissions. Unix permissions allow exactly one owner user and one owner group per file. If you need three different users with three different access levels on the same file, standard permissions can't express that. You need POSIX ACLs via

    setfacl
    and
    getfacl
    . The presence of a
    +
    at the end of the permission string in
    ls -l
    output signals an ACL is present on that file or directory.

    getfacl /opt/solvethenetwork/data/
    setfacl -m u:infrarunbook-admin:rwx /opt/solvethenetwork/data/
    setfacl -m u:deploy-agent:rx /opt/solvethenetwork/data/

    Knowing where standard permissions end and ACLs begin prevents a lot of frustration — and prevents the wrong workaround (like making a file world-writable) when the real answer is a targeted ACL entry.


    File permissions are foundational. Get them wrong and you're either breaking functionality or opening security holes — sometimes both at once, in ways that aren't immediately obvious. The details here — directory execute semantics, the correct interpretation of umask, SGID on shared directories, UMask in systemd units, the sticky bit — are the parts that matter in real operational work. When something breaks at 2 AM and the error message says "permission denied," you'll know exactly where to start.

    Frequently Asked Questions

    What is the difference between chmod 755 and chmod 644?

    chmod 755 grants the owner read, write, and execute permissions, while group and others get read and execute. This is typical for directories and executable scripts. chmod 644 grants the owner read and write, while group and others get read-only. This is standard for regular files like configs and web content that don't need to be executed.

    Why does removing a file require write permission on the directory, not the file itself?

    In Linux, a file is just an entry in a directory's namespace. Deleting a file means removing that directory entry, which is a write operation against the directory — not the file. The file's own permissions have no bearing on whether it can be deleted. This is why the sticky bit on /tmp is important: it restricts deletion to the file's owner even when the directory is world-writable.

    How does umask affect files created by systemd services?

    By default, systemd services inherit the system umask, which may not match what your application needs. You should explicitly set UMask= in the [Service] section of your unit file — for example UMask=0027 — to ensure files created by the service always have the intended permissions, independent of whatever the OS default is.

    What does a capital S or T mean in ls -l output?

    An uppercase S in the owner or group position means the setuid or setgid bit is set, but the corresponding execute bit is not. An uppercase T in the others position means the sticky bit is set but others execute is not. These combinations are usually unintentional and worth investigating — SUID or SGID without execute has no meaningful effect on most systems and may indicate a misconfiguration.

    Can root be restricted by file permissions?

    No. Root bypasses all standard Unix permission checks for read and write access. A file with mode 000 is still fully readable and writable by root. The only partial exception is execute: root needs at least one execute bit set somewhere on a binary to run it. If you need to restrict root's access to files or syscalls, that requires mandatory access control systems like SELinux or AppArmor, not standard chmod permissions.

    Related Articles