How to Use chmod to Change File Permissions on Fedora
You downloaded a shell script, ran it, and the terminal spat back Permission denied. Or you configured a local development server, restarted Apache, and your browser shows a 403 Forbidden error. You check the file permissions, they look correct, but the system still refuses to cooperate. This usually means you are fighting the permission bits, or you are ignoring SELinux. Fedora enforces strict access controls. Understanding how chmod interacts with the underlying permission model and the SELinux policy prevents hours of debugging.
What's actually happening
Unix permissions control who can read, write, or execute a file. Every file has three permission sets: owner, group, and others. Each set has three bits: read, write, and execute. The chmod command modifies these bits. It does not change ownership. It does not change SELinux contexts. It only adjusts the discretionary access control bits that the kernel checks first.
Think of permissions like access to a building. The owner has the master key. The group has a spare key hidden under the mat. Others have to knock on the door and hope someone lets them in. Read permission means you can see the contents. Write permission means you can modify or delete the contents. Execute permission means you can run the file as a program.
Directories behave differently than files. For a directory, read permission lets you list the files inside. Write permission lets you create or delete files inside. Execute permission lets you enter the directory and access the files within it. You can have read permission on a directory but no execute permission. In that case, you can see the filenames, but you cannot open any of them. This trap catches many new users.
The fix: symbolic and octal notation
Fedora supports two syntaxes for chmod. Symbolic notation describes the change relative to the current state. Octal notation sets the absolute state using numbers. Both work. Choose the one that matches your mental model.
Symbolic notation uses letters to represent users and permissions. u stands for user (owner). g stands for group. o stands for others. a stands for all. You combine these with operators: + to add, - to remove, and = to set exactly. This syntax is safe for surgical changes because it leaves other bits untouched.
Here's how to add execute permission for the owner and group without changing anything else.
chmod u+x,g+x /home/username/scripts/deploy.sh
# u+x adds execute permission for the owner only
# g+x adds execute permission for the group only
# The comma separates multiple operations in one command
# The file path points to the target script
Octal notation uses numbers to represent the permission bits. Each digit corresponds to a user class: owner, group, others. The digits are sums of read (4), write (2), and execute (1). A digit of 7 means 4+2+1, which grants read, write, and execute. A digit of 5 means 4+1, which grants read and execute. A digit of 6 means 4+2, which grants read and write. A digit of 0 grants nothing.
Here's how to set a script to be executable by everyone but writable only by the owner.
chmod 755 /home/username/scripts/deploy.sh
# 7 sets owner to read, write, and execute (4+2+1)
# 5 sets group to read and execute (4+1)
# 5 sets others to read and execute (4+1)
# This overwrites all existing permission bits
Use symbolic notation when you need to toggle a single bit without disturbing the rest of the permission mask. Use octal notation when you want to set the entire permission state to a known value in one step. Use the install command when you are deploying a new file and want to set permissions and ownership atomically.
Recursive changes and directory traps
The -R flag makes chmod recurse into subdirectories. This is useful for project folders, but it is also the fastest way to break a system. Recursive changes apply to every file and directory inside the target. If you set a directory to 755, the files inside also become 755. If those files are configuration files or private keys, you just exposed them.
Here's how to change permissions for a web project directory safely.
chmod -R 750 /var/www/myproject
# -R recurses into all subdirectories and files
# 750 grants owner full access, group read/execute, others nothing
# This ensures the web server group can read files
# Others cannot access the project directory at all
Be careful with the execute bit on directories. If you remove execute from a directory, users cannot traverse it. They cannot access files inside, even if the files have open permissions. This is a common cause of "Permission denied" errors that confuse users who only check the file permissions. The directory permissions must allow traversal for the file permissions to matter.
Avoid setting permissions to 777. This grants full access to everyone. It violates security best practices. It allows any user on the system to modify or delete the files. It may trigger SELinux denials or firewall alerts. World-writable directories are a security hole, not a convenience.
Special permission bits
Beyond read, write, and execute, there are three special bits: setuid, setgid, and sticky. These bits appear in the first column of ls -l output. They modify how the kernel handles execution and directory ownership.
Setuid (4) makes a file run with the permissions of the file owner, not the user who invoked it. This is dangerous. If a setuid binary has a vulnerability, an attacker can gain root access. Fedora disables setuid execution in many contexts by default. Only use setuid for system binaries that require elevated privileges, like passwd.
Setgid (2) on a file makes it run with the group permissions of the file owner. On a directory, setgid forces new files created inside to inherit the directory's group ownership. This is useful for shared project directories where multiple users need to collaborate.
Sticky (1) on a directory prevents users from deleting files they do not own. The /tmp directory uses the sticky bit. Anyone can create files in /tmp, but only the file owner or root can delete them. Without the sticky bit, any user could delete anyone else's temporary files.
Here's how to set the setgid bit on a shared directory so new files inherit the group.
chmod g+s /home/shared/project
# g+s adds the setgid bit for the group
# New files created inside will inherit the directory's group
# This simplifies collaboration without manual chown
# The sticky bit is not set, so group members can still delete files
Check the special bits before you change them. Accidentally removing setuid from a system binary can break login. Accidentally adding setuid to a user script can open a privilege escalation path.
SELinux interaction
Fedora uses SELinux to enforce mandatory access control. SELinux runs alongside traditional Unix permissions. If Unix permissions say yes but SELinux says no, the operation fails. You will see Permission denied in the terminal. The error looks identical to a standard permission failure. This misleads many users into changing permissions when the real issue is the SELinux context.
SELinux contexts are labels attached to files. They define what processes can access the file and how. The ls -Z command shows these contexts. If you move a file to a new location, the context often stays the same. The file might have the wrong context for its new purpose. chmod does not fix contexts. restorecon does.
Here's how to check the context and restore the default labels.
ls -Z /var/www/myproject
# ls -Z displays the SELinux context for each file
# Look for contexts that do not match the expected type
# For web content, expect httpd_sys_content_t
# Mismatched contexts cause access denials
sudo restorecon -Rv /var/www/myproject
# restorecon resets contexts based on the SELinux policy
# -R recurses into subdirectories
# -v prints verbose output showing changed files
# This fixes context mismatches without altering permissions
Run journalctl -t setroubleshoot to see SELinux denial messages. The output includes a one-line summary explaining what was blocked and how to fix it. Read those messages before you disable SELinux. Disabling SELinux is a nuclear option. It removes a critical security layer. Fix the context instead.
Check the SELinux context before you change permissions. A permission denial is often a context mismatch.
Verify it worked
After running chmod, verify the changes. Trust your eyes, not your memory. The ls -l command shows the permission bits. The stat command shows detailed metadata. Use stat when you need to confirm the exact octal value.
Here's how to verify the permissions and ownership.
ls -l /home/username/scripts/deploy.sh
# ls -l shows permissions, owner, group, size, and date
# The first column shows rwx bits for owner, group, others
# Check that the bits match your expectation
stat -c "%a %U:%G %n" /home/username/scripts/deploy.sh
# stat -c formats the output
# %a prints the octal permission mode
# %U prints the owner username
# %G prints the group name
# %n prints the filename
Try the operation that failed before. Run the script. Access the web page. If it still fails, check the SELinux context. Check the ownership. Check the parent directory permissions. The error is rarely the file permissions alone.
Common pitfalls and what the error looks like
The Permission denied error is generic. It does not tell you why access was denied. It could be the permission bits, the SELinux context, the ownership, or the parent directory permissions. Debugging requires checking each layer.
One common pitfall is changing permissions on a file you do not own. chmod requires ownership or root privileges. If you try to change permissions on a system file without sudo, the command fails immediately. The error is Operation not permitted. This is different from Permission denied. It means you lack the authority to modify the metadata, not that you lack access to the content.
Another pitfall is recursive changes on the wrong target. Running chmod -R 777 / is a catastrophic mistake. It makes every file on the system world-writable. It breaks setuid binaries. It exposes sensitive data. It can render the system unbootable. Always verify the path before using -R. Use tab completion to avoid typos.
A third pitfall is ignoring the execute bit on directories. Users often set files to 644 and directories to 755, but then change the directory to 644. The files are readable, but the directory is not traversable. Access fails. The fix is to ensure directories have the execute bit set for anyone who needs to enter them.
Avoid 777. World-writable directories are a security hole, not a convenience.
When to use this vs alternatives
Use chmod when you need to adjust the permission bits of an existing file or directory. Use chown when you need to change the owner or group of a file. Use restorecon when you suspect SELinux is blocking access despite correct permissions. Use the install command when you are copying a file and want to set permissions and ownership in a single atomic operation.
Use symbolic notation when you need to toggle a single bit without disturbing the rest of the permission mask. Use octal notation when you want to set the entire permission state to a known value in one step. Use the install command when you are deploying a new file and want to set permissions and ownership atomically.