UseToolSuite UseToolSuite

Linux File Permissions & chmod: A Developer's Practical Guide

Master Linux file permissions, octal notation, and chmod commands. Covers standard permission patterns for web servers, SSH keys, Docker, CI/CD, and advanced SUID/SGID concepts.

Necmeddin Cunedioglu Necmeddin Cunedioglu 9 min read
Part of the Developer Generators: The Tools That Save You Hours Every Week series

Practice what you learn

Chmod Calculator

Try it free →

Linux File Permissions & chmod: A Developer’s Practical Guide

The first time a developer deploys a web application to a Linux server, they almost inevitably encounter a mysterious “500 Internal Server Error” or “403 Forbidden” page. After hours of frantic debugging, checking logs, and rewriting configuration files, the root cause is finally discovered: a simple file permissions issue. The application process simply lacked the authority to read its own configuration file because the permissions were set to 600 instead of 644.

Linux’s permission architecture is elegant, secure, and unforgiving. While modern developers often rely on Docker or PaaS providers to abstract away OS-level concerns, understanding the UNIX permission model remains a non-negotiable skill for backend engineering, DevOps, secure CI/CD pipeline creation, and basic server administration.

This comprehensive guide breaks down the structure of Linux permissions, explains the mathematics of octal notation, provides battle-tested chmod templates for web servers and SSH keys, and dives into advanced architectural concepts like SUID and SGID.

The Anatomy of Linux Permissions

In Linux, everything is treated as a file — including directories, hardware devices, and sockets. Every file possesses ownership metadata linking it to a specific User (Owner) and a specific Group.

Alongside ownership, every file maintains three distinct permission sets defining what can be done to the file, and by whom.

When you run the ls -l command in a terminal, you see an output resembling this:

-rwxr-xr--  1  alice  developers  4096  Mar 15  deploy.sh
│├──┤├──┤├──┤
│  │   │   └─ Others (everyone else on the system)
│  │   └───── Group (members of the 'developers' group)
│  └───────── Owner (the user 'alice')
└──────────── File Type (- = file, d = directory, l = symlink)

The Three Permission Types

Each of the three sets (Owner, Group, Others) is granted a combination of three fundamental permissions:

SymbolPermissionWhat it means for a FileWhat it means for a Directory
rReadUser can view the file’s contentsUser can list the files inside the directory (ls)
wWriteUser can modify or truncate the fileUser can create, rename, or delete files within the directory
xExecuteUser can run the file as a programUser can enter the directory (cd) and access its files

Crucial Note on Directories: For a user to read a file inside a directory, they must have Execute permission on the parent directory, regardless of the permissions on the file itself. Without execute permission on a directory, it operates as a locked door.

Understanding Octal Notation (The Numbers)

While symbolic permissions (rwxr-xr--) are readable, typing them out in commands is tedious. Linux uses an elegant mathematical system called Octal Notation to represent permissions as a three-digit number.

The system assigns a binary value to each permission:

  • Read (r) = 4
  • Write (w) = 2
  • Execute (x) = 1
  • None (-) = 0

To determine the final number for a set, you simply add the values together.

Octal Calculation Example

Let’s calculate the octal value for rwxr-xr--:

  1. Owner (rwx): 4 (Read) + 2 (Write) + 1 (Execute) = 7
  2. Group (r-x): 4 (Read) + 0 (No Write) + 1 (Execute) = 5
  3. Others (r--): 4 (Read) + 0 (No Write) + 0 (No Execute) = 4

Therefore, the octal representation of rwxr-xr-- is 754.

Calculate visually: The math can get confusing. Use our interactive Chmod Calculator to toggle permissions visually and instantly copy the correct chmod command.

Standard Permission Patterns You Will Use Every Day

You do not need to memorize every possible combination. 95% of standard web development and server administration relies on these core permission numbers.

755 — Standard for Directories and Executable Scripts

chmod 755 /var/www/html
chmod 755 script.sh

Meaning: Owner can read, write, and execute. Everyone else can read and execute. Usage: This is the default requirement for web server document roots (so Apache/Nginx can serve the files) and for bash scripts that you want to be globally runnable.

644 — Standard for Regular Files

chmod 644 index.html
chmod 644 style.css

Meaning: Owner can read and write. Everyone else can only read. Usage: The absolute standard for static web assets (HTML, CSS, JS, images) and non-sensitive configuration files. The web server process (acting as “Others”) can read the file to serve it to users, but cannot maliciously overwrite it.

600 — High Security Private Files

chmod 600 .env
chmod 600 ~/.ssh/id_rsa

Meaning: Only the Owner can read and write. Group and Others have zero access. Usage: Mandatory for files containing secrets. If your .env file contains database passwords or API keys, it must be 600. Furthermore, the SSH protocol strictly enforces this; if an SSH private key has permissions looser than 600, the SSH client will actively refuse to use it and throw an error.

700 — High Security Private Directories

chmod 700 ~/.ssh/

Meaning: Only the Owner can enter the directory, list its contents, or modify files inside it. Usage: Used for the .ssh directory, secure user home directories, and database storage paths (like /var/lib/mysql).

Real-World Scenarios and Solutions

Scenario 1: Setting Up a Web Server (Nginx/Apache)

The most common cause of a 403 Forbidden error on a newly deployed website is incorrect file permissions. The web server process (usually running as www-data or nginx) must be able to read the files, but for security reasons, it should not own them or be able to write to them.

# Set ownership to your user, and the group to the webserver group
sudo chown -R myuser:www-data /var/www/html

# Recursively set all directories to 755
find /var/www/html -type d -exec chmod 755 {} \;

# Recursively set all regular files to 644
find /var/www/html -type f -exec chmod 644 {} \;

If your application needs an upload directory where users can save avatars or documents, you must grant write access to the web server group just for that specific folder:

# Allow the www-data group to write to the uploads folder
chmod 775 /var/www/html/wp-content/uploads

Scenario 2: Configuring SSH Keys Securely

SSH is brutally strict about permissions. A single misplaced bit will lock you out of your server.

chmod 700 ~/.ssh/                # The directory must be private
chmod 600 ~/.ssh/id_rsa          # Your private key (top secret)
chmod 644 ~/.ssh/id_rsa.pub      # Your public key (safe to share)
chmod 600 ~/.ssh/authorized_keys # The list of keys allowed to log in
chmod 600 ~/.ssh/config          # Your SSH client configuration

Scenario 3: Dockerfile Permissions

When building Docker images, developers often copy files as the root user. If your container runs as a non-root user for security (which it should), the application will crash attempting to read its own files.

# Create a non-root user
RUN adduser -D appuser

# Copy files and set permissions in a single layer to save space
COPY --chown=appuser:appuser --chmod=755 entrypoint.sh /app/
COPY --chown=appuser:appuser --chmod=644 package.json /app/
COPY --chown=appuser:appuser --chmod=600 .env.production /app/.env

USER appuser
CMD ["/app/entrypoint.sh"]

Scenario 4: CI/CD Pipelines (GitHub Actions / GitLab CI)

When a CI/CD runner checks out your git repository, shell scripts often lose their execute permissions depending on the host OS of the runner. Always explicitly add the execute bit before attempting to run a script in a pipeline.

# GitHub Actions Example
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run Deployment Script
        run: |
          chmod +x ./scripts/deploy.sh
          ./scripts/deploy.sh

The Three Catastrophic Mistakes Developers Make

Mistake 1: The “Lazy Fix” (chmod 777)

chmod 777 translates to: “Allow literally any user or process on this entire system to read, modify, delete, or execute this file.”

It is the permission equivalent of leaving your house’s front door wide open because you forgot your key. If an application throws a permission error, running chmod -R 777 /var/www will instantly fix it — and simultaneously create a serious security vulnerability. If a single plugin on your site gets compromised, the attacker now has write access to your entire codebase.

The Fix: Find out exactly which user the process is running as (e.g., ps aux | grep nginx), change the group ownership to that user, and grant 775 or 664 permissions instead.

Mistake 2: Destructive Recursive Chmod

A junior developer wants to fix web permissions and runs:

chmod -R 755 /var/www/html

While this fixes the directories, it accidentally makes every single .html, .css, and .jpg file on the server executable. This violates the principle of least privilege and confuses security scanning tools.

The Fix: Always use the find command to isolate directories from files before recursively applying permissions, as demonstrated in Scenario 1.

Mistake 3: The Umask Oversight

When you create a new file, why does it default to 644 instead of 666 or 777? This is controlled by the system’s umask (User File-Creation Mask).

If a backend daemon is creating log files with permissions that are too strict (preventing your monitoring agent from reading them), developers often try to hack a chmod command into the application code.

The Fix: Understand that the default umask is usually 022. This mask is subtracted from the base permission of 666 for files (yielding 644) and 777 for directories (yielding 755). If you need a daemon to create group-writable files, alter the daemon’s startup umask to 002.

Deep Dive: SUID, SGID, and Sticky Bits

Beyond the standard Read/Write/Execute bits, Linux employs three “special” permission bits that dramatically alter how processes and directories behave. These represent the most advanced and dangerous aspects of the permission architecture.

SUID (Set-User-ID)

SUID is represented by an s in the owner execute position, or by adding a 4 to the front of the octal string (e.g., chmod 4755).

When an executable binary file has the SUID bit set, it executes with the privileges of the file’s owner, rather than the privileges of the user who ran the command.

The Classic Example: The /usr/bin/passwd command. A normal user needs to change their password, which requires writing to the highly secure /etc/shadow file. Since normal users cannot write to /etc/shadow, the passwd binary is owned by root and has the SUID bit set. When a user runs it, the process temporarily elevates to root privileges just for the duration of the password change, allowing it to edit the shadow file safely.

The Danger: If a system administrator accidentally applies SUID to a text editor (like vim) or a shell (bash), it creates a serious vulnerability. Any low-level user could execute vim, instantly gain root privileges, and edit any file on the system.

SGID (Set-Group-ID)

SGID is represented by an s in the group execute position, or by adding a 2 to the octal string (e.g., chmod 2755).

While SGID can be applied to files (running the file with the permissions of the file’s group), it is vastly more useful when applied to directories in collaborative environments.

The Shared Directory Solution: Imagine a team of web developers sharing a /var/www/html directory. Developer A creates a file. By default, it is owned by Developer A’s primary group. Developer B tries to edit it and gets “Permission Denied.”

If you apply SGID to the directory:

chmod g+s /var/www/html

Any new file created within that directory automatically inherits the group ownership of the directory itself, rather than the primary group of the user who created it. If the directory is owned by the web-team group, every file inside will be owned by web-team, allowing seamless collaboration.

The Sticky Bit

The Sticky Bit is represented by a t at the end of the permission string, or by adding a 1 to the octal string (e.g., chmod 1777).

It is used almost exclusively on world-writable directories, most notably /tmp.

If /tmp is 777, what stops User A from deleting a temporary file created by User B? The Sticky Bit. When applied to a directory, it dictates that only the file’s owner, the directory’s owner, or the root user can rename or delete files within that directory. Everyone can write to /tmp, but you can only delete your own garbage.

Further Reading


Stop guessing permissions and breaking your servers. Use the interactive Chmod Calculator to safely generate the exact octal and symbolic commands you need before running them in production.

Necmeddin Cunedioglu
Necmeddin Cunedioglu Author
9 min read
-- views

Software developer and the creator of UseToolSuite. I write about the tools and techniques I use daily as a developer — practical guides based on real experience, not theory.