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:
- Owner (user) — The person who owns the file
- Group — A collection of users who share access
- 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,dfor 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
-
The most common fix is
chmod +x— 80% of “permission denied” errors on scripts are simply missing the execute bit. -
Always check ownership with
ls -lbefore trying anything else. Knowing the owner and group tells you exactly which permission set applies to you. -
Use
sudosparingly and intentionally — it’s a precision tool, not a blanket solution. Overusing it masks real permission problems.
4