Linux & OS Intermediate

Fixing ‘Permission Denied’ for Shell Scripts on Debian 12 Bookworm

Resolve 'Permission denied' errors when running shell scripts on Debian 12 Bookworm. Diagnose and fix file permissions, ownership, and filesystem mount options.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Resolve 'Permission denied' errors when running shell scripts on Debian 12 Bookworm. Diagnose and fix file permissions, ownership, and filesystem mount options.

Introduction

As an experienced Systems Administrator, encountering a "Permission denied" error when attempting to execute a shell script is a common hurdle. This issue prevents scripts from running, often halting automation, deployment processes, or critical system tasks. On Debian 12 Bookworm, while generally robust, specific configurations related to file permissions, ownership, or even the underlying filesystem can lead to this seemingly straightforward error. This guide provides a detailed, step-by-step approach to diagnose and resolve the "Permission denied" issue for shell scripts, ensuring your automated tasks run smoothly.

Symptom & Error Signature

When attempting to run a shell script, typically by invoking it directly from the terminal, you will encounter an error message similar to these examples:

# Attempting to execute directly
$ ./myscript.sh
bash: ./myscript.sh: Permission denied

or if the script is located in a different path:

$ /opt/scripts/deploy_app.sh
-bash: /opt/scripts/deploy_app.sh: Permission denied

Sometimes, even sudo might yield a similar result if the underlying issue is not just user permissions but a filesystem constraint:

$ sudo ./myscript.sh
sudo: ./myscript.sh: command not found
# (Or, if the path is explicit and permission is the core issue):
$ sudo /opt/scripts/deploy_app.sh
sudo: /opt/scripts/deploy_app.sh: command not found
# A more direct "Permission denied" via sudo for specific scenarios (less common)

Notice that if you execute the script by explicitly calling an interpreter (e.g., bash ./myscript.sh), it might work, hinting strongly that the problem is with the script's executable permission bit itself, rather than an issue with the script's syntax or the interpreter path.

Root Cause Analysis

The "Permission denied" error for script execution on Linux systems, including Debian 12, stems from a few primary causes:

  1. Missing Execute Permissions: This is by far the most common reason. For a file to be executed directly, its executable permission bit (x) must be set for the user attempting to run it (owner, group, or others). Without this, the operating system treats the file as data rather than an executable program.
  2. Incorrect File Ownership or Group Membership: If the script's owner or group is different from the user attempting to run it, and the "others" permissions do not include execute rights, access will be denied. The user might not be part of the file's owning group, or simply isn't the owner.
  3. Filesystem Mounted with noexec Option: A less obvious but critical cause is when the filesystem (partition) where the script resides is mounted with the noexec option. This security measure explicitly prevents the execution of any files on that filesystem, regardless of their individual file permissions. This is common for temporary directories like /tmp or sometimes for /var/www or user home directories in shared hosting environments.
  4. SELinux or AppArmor Restrictions (Advanced): While less common on a default Debian 12 installation (SELinux is typically absent, AppArmor is present but often permissive), security frameworks like AppArmor or SELinux (if installed and configured) can impose mandatory access control (MAC) policies that override standard discretionary access control (DAC) permissions, preventing script execution even if chmod +x is set.

Step-by-Step Resolution

Follow these steps sequentially to diagnose and resolve the "Permission denied" error for your shell scripts.

1. Verify Script Existence and Path Accuracy

First, ensure the script you are trying to execute actually exists at the specified path and that you haven't made a typo.

ls -l /path/to/your_script.sh

Replace /path/to/your_script.sh with the actual path to your script. If ls returns "No such file or directory", then the path is incorrect or the script is missing.

2. Check and Correct File Execute Permissions

This is the most frequent cause. The script must have the execute bit set for the user or group attempting to run it.

  1. Inspect Current Permissions:

    ls -l /path/to/your_script.sh
    

    Look at the first set of characters (e.g., -rwx------). The x indicates execute permission.

    • rwxr-xr-x: Owner can read, write, execute; Group and Others can read, execute. (Octal 755)
    • -rw-r--r--: No one can execute. (Octal 644) – This is the likely culprit.
  2. Identify Your User and Group:

    whoami
    groups $(whoami)
    

    This helps you determine which set of permissions (u for user/owner, g for group, o for others) applies to your execution attempt.

  3. Grant Execute Permissions: Use the chmod command to add execute permissions.

    • For the script owner (most common):
      chmod u+x /path/to/your_script.sh
      
    • For the script owner and group (common for shared projects):
      chmod ug+x /path/to/your_script.sh
      
    • For all users (owner, group, and others):
      chmod a+x /path/to/your_script.sh
      
      or using octal notation (e.g., 755 for rwxr-xr-x):
      chmod 755 /path/to/your_script.sh
      

    While chmod 777 grants full read, write, and execute permissions to everyone, it is generally a security risk and should be avoided in production environments unless absolutely necessary and understood. Stick to the principle of least privilege.

  4. Verify and Retest: After applying chmod, run ls -l again to confirm the permissions, then try executing the script.

3. Verify File Ownership and Group

If the permissions are correct but the script is owned by a different user/group and the "others" permissions are restrictive, you might still get "Permission denied".

  1. Check Current Ownership:

    ls -l /path/to/your_script.sh
    

    The output will show the owner and group (e.g., -rwxr-xr-x 1 owner group ...).

  2. Change Ownership (if necessary): If the current owner/group is not the user or a group the user belongs to, you may need to change it. This requires sudo privileges.

    • To change owner to your current user:
      sudo chown $(whoami):$(whoami) /path/to/your_script.sh
      
      (This sets both user and group to your current user)
    • To change owner to a specific user and group:
      sudo chown newuser:newgroup /path/to/your_script.sh
      

    Incorrectly changing ownership of system-critical files can lead to system instability or security vulnerabilities. Exercise caution.

  3. Retest: After adjusting ownership, retry executing the script.

4. Check Filesystem Mount Options for noexec

This is a critical step if the above permission fixes don't work. The filesystem itself might be preventing execution.

  1. Determine the Mount Point: Identify which filesystem your script resides on.

    df -h /path/to/your_script.sh
    

    This will show the filesystem name and its mount point. For example, if your script is in /var/www/html/scripts/, the mount point might be /var.

  2. Inspect Mount Options: Check the mount options for that filesystem.

    findmnt -M /path/to/your_script.sh
    # Alternatively:
    # mount | grep /path/to/your_script.sh
    # (If the path is within a mounted partition)
    # Or more generally:
    # mount
    

    Look for noexec in the "OPTIONS" column. If present (e.g., rw,noexec,relatime), this is the problem.

  3. Remount Filesystem without noexec: This requires sudo privileges and can temporarily disrupt access to that filesystem.

    • Temporary Fix (until next reboot):

      sudo mount -o remount,exec /mount/point
      

      Replace /mount/point with the actual mount point (e.g., /var).

    • Persistent Fix (modifying /etc/fstab): For a permanent solution, you need to edit /etc/fstab.

      sudo nano /etc/fstab
      

      Locate the line corresponding to your filesystem. For example: UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /var ext4 defaults,noexec 0 2 Change noexec to exec (or remove noexec if defaults doesn't imply it). UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /var ext4 defaults,exec 0 2

      After saving /etc/fstab, apply the changes without rebooting:

      sudo mount -a
      

      Modifying /etc/fstab incorrectly can render your system unbootable. Always make a backup of /etc/fstab before editing: sudo cp /etc/fstab /etc/fstab.bak

  4. Verify and Retest: After remounting, confirm with findmnt -M /mount/point that noexec is gone, then try running your script.

5. Verify Shebang Line (Interpreter Path)

While not strictly a "Permission denied" issue, a malformed or missing shebang line can lead to confusion or different errors, especially if the system tries to execute it without knowing the interpreter. Ensure your script starts with a valid shebang, like:

#!/bin/bash

or

#!/usr/bin/env bash

Also, ensure the interpreter specified in the shebang actually exists:

which bash

If which bash returns nothing, then /bin/bash or /usr/bin/bash might not be correct, or bash isn't installed. For Debian 12, bash is usually at /bin/bash.

6. Check for SELinux or AppArmor Interference (Advanced)

If you've exhausted all other options and are on a hardened system, mandatory access control (MAC) policies might be at play.

  1. Check SELinux Status:

    sestatus
    

    If SELinux is enabled, investigate logs (e.g., sudo journalctl -t selinux -e) for AVC denials related to your script's execution. You might need to adjust SELinux contexts with chcon or create custom policies. (SELinux is not enabled by default on Debian 12).

  2. Check AppArmor Status:

    sudo aa-status
    

    If AppArmor is active and your script's path falls under a restrictive profile, you might see denials in dmesg or journalctl. You would need to audit the relevant AppArmor profile (e.g., located in /etc/apparmor.d/) and potentially modify it or place the script outside its protected path.

    Investigating and managing SELinux or AppArmor policies requires advanced system administration knowledge. Only proceed if you are familiar with these security frameworks.

By systematically working through these steps, you should be able to identify and resolve the "Permission denied" error, allowing your shell scripts to execute as intended on Debian 12 Bookworm.

👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.