Understanding Linux File Permissions on Fedora (rwx, chmod, chown)

Use chmod to set rwx permissions and chown to change file ownership on Fedora Linux.

When a script refuses to run or a config file stays locked

You drop a new deployment script into your home directory, run it, and get bash: ./deploy.sh: Permission denied. You try to edit a service configuration and your editor complains the file is read-only. You check the file and see -rw-r--r--. You know you need to change permissions, but the octal numbers like 755 or 644 feel like arbitrary passwords. The Linux permission model is straightforward once you map the three columns to the three actors the kernel recognizes. You just need to understand how the kernel evaluates access before you start guessing with chmod.

How the permission model actually works

Linux tracks access through three sets of three bits. Each set controls read, write, and execute for a specific scope. The first set belongs to the file owner. The second set belongs to the group that owns the file. The third set belongs to everyone else on the system. Think of it like a building with three key rings. The owner has a master key ring that opens every door. The group has a shared key ring that opens the common areas. Everyone else gets a basic key that only opens the lobby.

The kernel checks these keys in a strict order. It first compares the process user ID to the file owner. If they match, the kernel uses the first column of permissions. If they do not match, the kernel checks whether the process belongs to the file group. If yes, it uses the second column. If neither matches, it falls back to the third column. The check stops at the first match. Group permissions never apply if you are the owner. Others permissions never apply if you are in the group.

The r, w, and x letters in ls -l output map directly to those keys. Read lets you view the contents. Write lets you modify or delete. Execute lets the kernel run the file as a program or treat a directory as a path you can traverse. Directories behave differently than regular files. Execute on a directory does not run the directory. It allows the kernel to traverse it. Without execute on a parent directory, you lose access to every file inside it, even if the files themselves show full permissions.

Check the permission string before you assume the file is broken. Half the time the issue is a missing execute bit on a parent folder, not the target file itself.

Changing permissions with chmod

The chmod command modifies those three sets of bits. You can use symbolic notation like u+x or octal notation like 755. Octal is faster for bulk changes and matches the way most documentation writes permissions. Each digit represents a sum of the bits: 4 for read, 2 for write, 1 for execute. A digit of 7 means 4+2+1, which is full access. A digit of 5 means 4+1, which is read and execute. A digit of 6 means 4+2, which is read and write.

Here is how to check the current state before making changes.

ls -l /path/to/script.sh
# WHY: Shows the current permission string, owner, group, and size.
# WHY: The first character indicates file type (- for regular, d for directory).
# WHY: The next nine characters are the rwx bits for owner, group, and others.

Here is how to apply standard script permissions using octal notation.

sudo chmod 755 /path/to/script.sh
# WHY: 7 grants owner full read, write, and execute access.
# WHY: 5 grants group read and execute access, blocking modification.
# WHY: 5 grants others read and execute access, keeping the script public but safe.
# WHY: sudo is required if you do not own the file or the directory it lives in.

Here is how to restrict a sensitive configuration file to the owner only.

sudo chmod 600 /etc/myapp/secrets.conf
# WHY: 6 gives the owner read and write access, removing execute.
# WHY: 0 strips all permissions from the group, preventing shared access.
# WHY: 0 strips all permissions from others, blocking system-wide reads.
# WHY: Config files in /etc/ are user-modified. Files in /usr/lib/ ship with packages. Always edit /etc/.

Symbolic notation works better when you need to toggle a single bit without rewriting the whole mask. Adding or removing execute permission for the owner does not touch the group or others columns.

Here is how to add execute permission for the current user only.

chmod u+x /path/to/script.sh
# WHY: u targets the owner column specifically.
# WHY: +x appends the execute bit without altering existing read or write flags.
# WHY: This preserves the original group and others permissions exactly as they were.

New files inherit permissions from your shell umask. The default Fedora umask is 022, which strips write permission from group and others. That is why text editors create files as 644 and why compiled binaries often land as 755. You rarely need to change the umask unless you are working in a shared development environment where group write access is required from the start.

Run umask to see your current mask. Adjust it in ~/.bashrc only if your workflow demands it.

Changing ownership with chown

Permissions only matter if the right user owns the file. The chown command changes the user and group that the kernel associates with the inode. You cannot change ownership of a file you do not own, so sudo is mandatory for system paths. The syntax accepts a username, a group name, or both separated by a colon.

Here is how to transfer ownership of a project directory to a specific user and group.

sudo chown -R developer:devteam /srv/projects/webapp
# WHY: -R applies the change recursively to every file and subdirectory inside.
# WHY: developer becomes the new owner, gaining the first column of permissions.
# WHY: devteam becomes the new group, gaining the second column of permissions.
# WHY: Recursive changes on large directories can take time. Run this during off-hours if the tree is massive.

Here is how to change only the group ownership while keeping the user intact.

sudo chgrp devteam /srv/projects/webapp/config.yml
# WHY: chgrp is a dedicated alias for changing only the group field.
# WHY: The colon syntax in chown works too, but chgrp reads cleaner for single-field updates.
# WHY: Group changes are useful when multiple users need shared write access to a workspace.

Package managers like dnf reset ownership during upgrades. If you manually change ownership of a file in /usr/bin or /etc, the next dnf upgrade will overwrite your change. Keep custom ownership adjustments in /home, /srv, or /opt to avoid fighting the package manager.

Verify the owner before you restart services. A daemon running as the wrong user will fail to bind to ports or read its own logs.

Verify it worked

The terminal does not guess. You must confirm the kernel actually applied the changes. ls -l shows the permission string, but stat reveals the exact octal values and inode metadata. Run stat after every permission change to catch silent failures or unexpected overrides.

Here is how to verify the exact permission mask and ownership.

stat -c "%A %a %U:%G %n" /path/to/script.sh
# WHY: %A prints the human-readable permission string like -rwxr-xr-x.
# WHY: %a prints the numeric octal mask like 755.
# WHY: %U and %G print the resolved username and group name.
# WHY: %n prints the filename so you can verify you are looking at the right path.

If you are troubleshooting a service that refuses to start, check the exact path the service file references. Symlinks resolve to the target inode, but the permission check happens on the final file. A broken symlink or a permission mismatch on the target will surface as a generic access error.

Run stat on the actual binary or config path. Match the output against your service requirements before editing unit files.

Common pitfalls and what the error looks like

The most frequent mistake is confusing directory permissions with file permissions. Execute permission on a directory does not run the directory. It allows the kernel to traverse it. If you remove execute from a parent directory, you lose access to every file inside it, even if the files themselves show 777. You will see Permission denied when trying to cd into it or list its contents.

Another trap is the sticky bit and setuid/setgid flags. Web servers and shared upload directories use the sticky bit to prevent users from deleting each other's files. System binaries like passwd use setuid to temporarily run with root privileges. Changing these bits with a blanket chmod 777 breaks security controls and triggers audit warnings.

-rwsr-xr-x. 1 root root 27952 Jun 12 10:00 /usr/bin/passwd

The s in the execute position means setuid is active. The kernel runs the binary as the file owner instead of the calling user. Strip that bit and the password change command fails silently or drops to a non-privileged state.

SELinux also intercepts access decisions. Traditional rwx bits are the first gate. SELinux labels are the second gate. If you move a web server document to a custom directory and Apache refuses to serve it, check the context before blaming chmod. Run ls -Z to see the security label. A mismatched label will block access even with 777 permissions.

ls -Z /var/www/html/index.html
# WHY: -Z appends the SELinux security context to the standard listing.
# WHY: The context string looks like user:role:type:level.
# WHY: Apache expects httpd_sys_content_t for static files.
# WHY: Restoring the correct label with restorecon fixes the block without disabling SELinux.

SELinux denials show up in journalctl -t setroubleshoot with a one-line summary. Read those before disabling SELinux. The default Fedora policy is permissive enough for desktop use but strict for services. Trust the policy. Fix the label instead of lowering the wall.

Check the journal before you touch permissions. The actual denial reason is usually logged two lines above the generic error message.

When to use which tool

Use chmod when you need to adjust read, write, or execute access for existing owners, groups, or public users. Use chown when a file belongs to the wrong user account after a migration or a package reinstall. Use chgrp when you only need to shift group membership without touching the owner field. Use chmod with symbolic notation when you are toggling a single bit and want to preserve the rest of the mask. Use chmod with octal notation when you are applying a known standard like 644 for configs or 755 for scripts. Use stat when you need to verify the exact numeric mask before automating a deployment step. Use restorecon when SELinux blocks access despite correct rwx bits. Use umask configuration when your workflow requires group write access on newly created files.

Apply the right tool to the exact layer that is failing. Permissions, ownership, and security contexts are separate gates. Fix them in order.

Where to go next