"Permission Denied" Running .sh Script on Linux
"Permission denied" when trying to run a shell script almost always means the file itself isn't marked as executable — Linux requires explicit execute permission on a file before it can be run directly, regardless of what's inside it.
The Problem
You try to run a script you just wrote or downloaded:
./deploy.sh
bash: ./deploy.sh: Permission denied
The file clearly exists and its contents look correct when you open it, but the shell refuses to execute it.
Why It Happens
Unlike some operating systems, Linux doesn't determine whether a file is "runnable" based on its extension (like .sh) — it checks the file's actual permission bits. The most common causes are:
- The file simply doesn't have execute permission set, which is the default for newly created files or files transferred from another system (like downloading from GitHub or copying via certain tools)
- The file is on a filesystem mounted with options that ignore execute permissions entirely, common with some network drives, USB drives formatted as FAT/NTFS, or certain Docker volume configurations
- You're running the script as a user who doesn't have execute permission specifically, even though the file owner does (permission bits are separate for owner, group, and others)
The Fix
First, check the current permissions to confirm execute is actually missing:
ls -l deploy.sh
-rw-r--r-- 1 user user 245 Jul 27 12:00 deploy.sh
The absence of any x in the permission string (-rw-r--r--) confirms no one has execute permission on this file. Add execute permission for yourself (and optionally others):
chmod +x deploy.sh
Or, to be more specific about who gets execute permission:
chmod u+x deploy.sh # owner only
chmod ug+x deploy.sh # owner and group
After this, ls -l should show an x in the permission string, and ./deploy.sh should run.
If you don't want to modify the file's permissions (for example, a script you downloaded that you're just testing once), you can run it directly through the interpreter instead, which bypasses the execute permission requirement entirely, since you're not executing the file directly but passing it as an argument to bash:
bash deploy.sh
If the file is on a filesystem that ignores Unix permissions (common with FAT32/NTFS-formatted drives or certain network mounts), chmod may appear to succeed but have no actual effect. Check how the filesystem is mounted:
mount | grep /path/to/mount/point
Look for mount options like noexec, which explicitly blocks execution regardless of file permissions — in that case, copy the script to a regular Linux filesystem location (like your home directory) before running it, rather than trying to execute it directly from that mount.
Still Not Working?
If chmod +x succeeds and permissions show correctly with ls -l, but you still get "Permission denied," check the script's shebang line (the first line, like #!/bin/bash) for hidden formatting issues — a script edited or transferred from Windows can end up with Windows-style line endings (\r\n instead of \n), which corrupts the shebang line and produces a permission or "command not found" error that looks unrelated to line endings at first glance:
sed -i 's/\r$//' deploy.sh
It's also worth checking whether the parent directory itself has execute permission, since Linux requires execute permission on every directory in the path leading to the file, not just on the file itself. If any directory along the way is missing execute permission for your user, the shell can't traverse into it to reach the script at all:
ls -ld /path/to/parent/directory
Look for an x in the permission string for the relevant category (owner, group, or others, depending on who you're running as). Missing directory execute permission produces the same "Permission denied" message as a missing file execute bit, which can be confusing since the fix is different — chmod +x on the directory, not the script itself, in that case.
Finally, if you're running the script through sudo and still hitting this error, confirm the issue isn't actually SELinux or AppArmor enforcement on systems where those are active, since both can block execution independently of standard Unix permissions, with error messages that don't always clearly indicate a security module is the actual cause:
sudo dmesg | grep -i denied