How to Fix Permission Denied Linux Terminal: A Complete Troubleshooting Guide

How to Fix Permission Denied Linux Terminal: A Complete Troubleshooting Guide

We’ve all been there. You’re in the middle of deploying a critical update, you fire off a command in your terminal with confidence, and then — BAM — the system slaps you with a cold, unhelpful error:

bash: ./deploy.sh: Permission denied

Or maybe:

-bash: /var/log/app.log: Permission denied

If you’re searching for how to fix permission denied linux terminal, you’re not alone. This is one of the most common issues developers face when working with Linux, whether on a VPS, a local WSL setup, or a containerized environment.

In this guide, I’ll walk you through the root causes, the exact fixes (ordered from most common to edge cases), and how to prevent this headache from happening again. Let’s get your terminal back in business.


Understanding Linux File Permissions (The Foundation)

Before diving into fixes, you need to understand what Linux is actually telling you. When you see “Permission denied,” the operating system is enforcing its security model — and that’s a good thing, even when it’s annoying.

The Three Permission Types

Linux has three core permission types:

  • Read (r) — View the contents of a file or list a directory
  • Write (w) — Modify a file or add/remove entries in a directory
  • Execute (x) — Run a file as a program or enter a directory

The Three User Categories

Permissions are assigned to three categories:

  1. Owner (user) — The person who owns the file
  2. Group — A collection of users who share access
  3. Others — Everyone else on the system

Reading Permission Output

Run ls -l on any file and you’ll see something like this:

$ ls -l deploy.sh
-rw-r--r-- 1 sarah developers 2048 Jan 15 10:30 deploy.sh

Breaking down -rw-r--r--:

  • Position 1: File type (- for regular file, d for directory)
  • Positions 2-4 (rw-): Owner permissions (read + write, no execute)
  • Positions 5-7 (r--): Group permissions (read only)
  • Positions 8-10 (r--): Others permissions (read only)

Notice that there’s no x anywhere. That’s exactly why running ./deploy.sh fails with “Permission denied.”


Root Cause Analysis: Why Permission Denied Happens

The “Permission denied” error can stem from several different root causes. Identifying the correct one is half the battle.

Cause 1: Missing Execute Permission on Scripts

This is the #1 most common cause, especially for developers who just downloaded a shell script from a repository, transferred a file via SCP, or created a new executable.

$ ./install.sh
bash: ./install.sh: Permission denied

Linux won’t run a file as a program unless it has the execute bit set. This is a security feature — imagine if any file you downloaded could auto-execute.

Cause 2: Insufficient User Privileges

You’re trying to access system files, write to protected directories, or modify files owned by another user (like root).

$ echo "test" >> /etc/hosts
bash: /etc/hosts: Permission denied

Cause 3: Filesystem Mounted with Restrictions

Sometimes the file itself has correct permissions, but the filesystem is mounted read-only or with options like noexec.

$ mount | grep /mnt/data
/dev/sdb1 on /mnt/data type ext4 (ro,noexec)

Cause 4: SELinux or AppArmor Blocking Access

On distributions like CentOS, RHEL, Fedora (SELinux) or Ubuntu (AppArmor), mandatory access control systems can override standard file permissions.

$ cat /var/log/audit/audit.log | grep denied
type=AVC msg=audit(1705312200.123:456): avc:  denied  { execute } for  pid=1234 comm="bash" path="/opt/app/deploy.sh" scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0

Cause 5: ACL (Access Control List) Overrides

Standard ls -l output doesn’t show ACLs. A file might look accessible but have an ACL that explicitly denies your user.

$ getfacl project.config
# file: project.config
# owner: john
# group: developers
user::rw-
user::---
group::r--
mask::r--
other::r--

Cause 6: File Locking or Immutable Flag

Files can be set as immutable, which not even root can modify without removing the attribute first.

$ lsattr important.conf
----i--------e----- important.conf

That i means immutable — no writes allowed, period.


Step-by-Step Solutions: From Most Common to Edge Cases

Now let’s work through the fixes in order of likelihood.


Solution 1: Add Execute Permission with chmod

Best for: Shell scripts, binaries, and any file you’re trying to run directly.

If you’re trying to execute a script and getting “Permission denied,” the fix is almost always chmod:

# Make the file executable for the owner
chmod u+x deploy.sh

# Make it executable for everyone
chmod +x deploy.sh

# Set standard executable permissions (owner can read/write/execute, others can read/execute)
chmod 755 deploy.sh

# Set restrictive executable permissions (only owner can execute)
chmod 700 deploy.sh

After running the command, verify:

$ ls -l deploy.sh
-rwxr-xr-x 1 sarah developers 2048 Jan 15 10:30 deploy.sh

Now try running it again:

$ ./deploy.sh
Deployment started...

Pro Tip: Understanding chmod Numeric Notation

Number Permission Symbolic
0 No permission ---
1 Execute only --x
2 Write only -w-
3 Write + Execute -wx
4 Read only r--
5 Read + Execute r-x
6 Read + Write rw-
7 Read + Write + Execute rwx

Solution 2: Use sudo for System-Level Operations

Best for: Writing to /etc, /var, /opt, installing packages, or modifying root-owned files.

# Instead of this:
$ nano /etc/nginx/nginx.conf
bash: /etc/nginx/nginx.conf: Permission denied

# Do this:
$ sudo nano /etc/nginx/nginx.conf

For redirections, sudo alone won’t work because the shell handles redirections before sudo kicks in:

# This WON'T work:
$ sudo echo "127.0.0.1 myapp.local" >> /etc/hosts
bash: /etc/hosts: Permission denied

# This WILL work:
$ echo "127.0.0.1 myapp.local" | sudo tee -a /etc/hosts

# Or use sudo with a subshell:
$ sudo bash -c 'echo "127.0.0.1 myapp.local" >> /etc/hosts'

Important: Don’t Overuse sudo

I’ve seen junior developers reflexively prepend sudo to every command. This is dangerous — it bypasses the permission system that protects you. Only use sudo when you genuinely need elevated privileges.


Solution 3: Change File Ownership with chown

Best for: Files transferred between users, files created by a different service, or files you need persistent access to.

If you’re consistently getting permission denied on files you should own, check the current owner:

$ ls -l /var/www/html/index.html
-rw-r--r-- 1 root root 4096 Jan 15 10:30 /var/www/html/index.html

The file is owned by root, but you’re www-data or your own user. Change ownership:

# Change owner to current user
sudo chown $USER:$USER /var/www/html/index.html

# Recursively change ownership of an entire directory
sudo chown -R $USER:$USER /var/www/html/

# Change ownership to a specific user and group
sudo chown www-data:www-data /var/www/html/index.html

Solution 4: Fix Group Permissions for Collaborative Access

Best for: Team environments where multiple developers need access to the same files.

Instead of relying on others permissions, create a shared group:

# Create a developers group (if it doesn't exist)
sudo groupadd developers

# Add users to the group
sudo usermod -aG developers sarah
sudo usermod -aG developers john

# Set the group ownership of the project directory
sudo chgrp -R developers /opt/project/

# Give the group read/write/execute access
sudo chmod -R 775 /opt/project/

# Set the SGID bit so new files inherit the group
sudo chmod g+s /opt/project/

After adding yourself to a group, you need to log out and back in (or use newgrp):

$ newgrp developers

Solution 5: Remount a Read-Only or noexec Filesystem

Best for: External drives, mounted volumes, or Docker volumes that refuse to let you execute files.

Check how the filesystem is mounted:

$ mount | grep /mnt/data
/dev/sdb1 on /mnt/data type ext4 (ro,noexec)

The ro means read-only and noexec prevents execution. Remount with proper options:

# Remount as read-write with execute allowed
sudo mount -o remount,rw,exec /mnt/data

For persistent changes, edit /etc/fstab:

# Find the entry for your mount
cat /etc/fstab | grep /mnt/data
/dev/sdb1    /mnt/data    ext4    ro,noexec    0    2

# Edit to:
sudo nano /etc/fstab
/dev/sdb1    /mnt/data    ext4    defaults    0    2

Then remount:

sudo mount -o remount /mnt/data

Solution 6: Resolve SELinux Context Issues

Best for: RHEL, CentOS, Fedora, and Rocky Linux systems where standard permissions look correct but access still fails.

Check if SELinux is the culprit:

# Check SELinux status
$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
Current mode:                   enforcing

# Check for denied operations
$ sudo ausearch -m avc -ts recent

If SELinux is blocking access, you can fix the file context:

# Restore default SELinux context for a file
sudo restorecon -v /opt/app/deploy.sh

# For an entire directory
sudo restorecon -Rv /opt/app/

If you need to set a specific context (e.g., allowing a script to run in the httpd context):

sudo semanage fcontext -a -t httpd_sys_script_exec_t "/opt/app(/.*)?"
sudo restorecon -Rv /opt/app/

For quick debugging (not recommended for production), you can temporarily set SELinux to permissive:

sudo setenforce 0

This logs violations instead of blocking them. Set it back with:

sudo setenforce 1

Solution 7: Clear Restrictive ACLs

Best for: Files where ls -l shows correct permissions but access still fails.

Check for ACLs:

$ getfacl database.yml
# file: database.yml
# owner: john
# group: developers
user::rw-
user:sarah:---     # <-- Sarah is explicitly denied!
group::rw-
mask::rw-
other::---

Remove the restrictive ACL:

# Remove a specific user ACL
setfacl -x u:sarah database.yml

# Remove all ACLs (revert to standard permissions)
setfacl -b database.yml

# Recursively remove ACLs from a directory
setfacl -R -b /opt/project/

Solution 8: Remove the Immutable Flag

Best for: Configuration files that refuse to be modified even as root.

Check for immutable attributes:

$ lsattr /etc/important.conf
----i--------e----- /etc/important.conf

Remove the immutable flag:

sudo chattr -i /etc/important.conf

Now you can modify the file normally. If you need to protect it again later:

sudo chattr +i /etc/important.conf

Solution 9: Fix SSH Key Permissions

Best for: SSH authentication failures that report “Permission denied (publickey).”

SSH is extremely strict about key file permissions. If they’re too open, SSH refuses to use them:

$ ssh -i ~/.ssh/id_rsa user@server
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0644 for '/home/sarah/.ssh/id_rsa' are too open.

Fix the permissions:

# Private key must be 600
chmod 600 ~/.ssh/id_rsa

# Public key should be 644
chmod 644 ~/.ssh/id_rsa.pub

# .ssh directory must be 700
chmod 700 ~/.ssh/

# authorized_keys must be 600
chmod 600 ~/.ssh/authorized_keys

Solution 10: Handle Docker and Container Permission Issues

Best for: Docker volumes, bind mounts, and container user mismatches.

A common scenario: you mount a volume, but the container runs as a different UID:

$ docker run -v /home/sarah/data:/app/data myapp
Permission denied: /app/data/config.json

The container user (often UID 1000 or a service user) doesn’t match the host file owner. Fix options:

# Option 1: Run the container as your host UID
docker run --user $(id -u):$(id -g) -v /home/sarah/data:/app/data myapp

# Option 2: Change the host file ownership to match the container user
sudo chown -R 1000:1000 /home/sarah/data/

# Option 3: Make the files accessible to everyone (less secure)
chmod -R 777 /home/sarah/data/

Prevention Tips: Avoiding Permission Denied Errors

Fixing permissions after the fact is reactive. Here’s how to prevent these issues from happening in the first place.

1. Use umask to Set Default Permissions

Your umask determines the default permissions for newly created files:

# Check current umask
$ umask
0022

# Set a more permissive umask for team directories (022 is restrictive)
umask 0002

# Make it persistent by adding to ~/.bashrc or ~/.zshrc
echo "umask 0002" >> ~/.bashrc

With umask 022, new files get 644 and directories get 755. With umask 002, new files get 664 and directories get 775 — giving group write access.

2. Use a Consistent User Setup in Docker

# Don't run as root in production containers
FROM node:20-slim

# Create a non-root user with a specific UID
RUN groupadd -r appuser && useradd -r -g appuser -u 1000 appuser

# Set proper ownership before switching users
COPY --chown=appuser:appuser . /app
WORKDIR /app

USER appuser
CMD ["node", "server.js"]

3. Set Up Project Directories with SGID

# Create a shared project directory
sudo mkdir -p /opt/shared-project

# Set group ownership
sudo chgrp -R developers /opt/shared-project

# Set SGID so new files inherit the group
sudo chmod 2775 /opt/shared-project

# Set default ACLs for new files
sudo setfacl -d -m g:developers:rwx /opt/shared-project

4. Use Git Hooks to Enforce Execute Permissions

If you’re working with scripts in a Git repository, ensure execute bits are preserved:

# Make a script executable and tell Git to track it
chmod +x scripts/deploy.sh
git update-index --chmod=+x scripts/deploy.sh
git commit -m "Make deploy.sh executable"

5. Audit Permissions Regularly

Create a simple script to audit common problem areas:

#!/bin/bash
# permission-audit.sh — Run weekly to catch permission issues

echo "=== World-writable files in /etc ==="
find /etc -perm -002 -type f 2>/dev/null

echo "=== Files with no owner ==="
find / -nouser -o -nogroup 2>/dev/null | head -20

echo "=== SSH key permissions ==="
ls -la ~/.ssh/

echo "=== SUID binaries ==="
find / -perm -4000 -type f 2>/dev/null

Key Takeaways

  1. The most common fix is chmod +x — 80% of “permission denied” errors on scripts are simply missing the execute bit.

  2. Always check ownership with ls -l before trying anything else. Knowing the owner and group tells you exactly which permission set applies to you.

  3. Use sudo sparingly and intentionally — it’s a precision tool, not a blanket solution. Overusing it masks real permission problems.

4

Leave a Reply

Your email address will not be published. Required fields are marked *