Permission denied errors when running Django with Gunicorn usually stem from file ownership, socket permissions, or user/group mismatches. This guide walks through diagnosing and fixing the most common causes so your app runs smoothly.
What You’ll Need
- Access to the server (SSH or terminal)
- sudo privileges (for chown, chmod, systemctl commands)
- Basic familiarity with your Django project structure
- Gunicorn already installed (if not, install it with pip)
Step-by-Step
- Identify the exact error message. Run Gunicorn in the foreground (e.g.,
gunicorn myproject.wsgi) and note the full error. Look for phrases like “Permission denied”, “[Errno 13]”, or “Can’t connect to …”. The error will tell you which file or socket is causing the problem. - Check file ownership. Use
ls -lon your project directory and on the socket file (if you use one). The user that runs Gunicorn must have read (and often write) access to the project files. If not, change ownership:sudo chown -R www-data:www-data /path/to/your/project
Replace
www-datawith the user you run Gunicorn as (e.g.,ubuntu,deploy). - Verify directory permissions. Ensure the project directory is traversable (execute permission) for the Gunicorn user. Set reasonable permissions:
sudo chmod -R 755 /path/to/your/project
For directories where Django needs to write (like media or static), use
chmod -R 775orchmod -R 770depending on your group setup. - Check the socket file and its directory. If you’re using a Unix socket (e.g., in a systemd service file), the directory containing the socket must be writable by the Gunicorn user. Common locations:
/run/gunicorn/or/tmp/. Create the directory and set ownership:sudo mkdir -p /run/gunicorn sudo chown www-data:www-data /run/gunicorn sudo chmod 775 /run/gunicorn
Also ensure the socket file itself (if it already exists) is owned by the Gunicorn user.
- Review your Gunicorn service file. If using systemd, open the service file (e.g.,
/etc/systemd/system/gunicorn.service) and check theUser=,Group=, andWorkingDirectory=directives. They must match the ownership you set. Example:[Service] User=www-data Group=www-data WorkingDirectory=/path/to/your/project ExecStart=/path/to/venv/bin/gunicorn --workers 3 --bind unix:/run/gunicorn/myproject.sock myproject.wsgi:application
After editing, run
sudo systemctl daemon-reloadand restart Gunicorn. - Check SELinux or AppArmor. On some distributions, security modules block Gunicorn from accessing certain paths. Temporarily disable SELinux to test:
sudo setenforce 0. If it works, you’ll need to adjust SELinux policies or set the correct context. For AppArmor, check if Gunicorn is confined and adjust accordingly. - Test with a simple static file. Create a temporary file in your project root (e.g.,
touch test.txt) and try to read it with the Gunicorn user:sudo -u www-data cat test.txt. If that fails, the issue is with directory permissions, not Gunicorn itself. - Check for read-only file systems. If your project is on a read-only mount (like some container setups), you’ll see permission errors even with correct ownership. Check with
mount | grep /pathand remount with write access if needed. - Verify Python environment access. Ensure the Gunicorn user can read the virtual environment. Run
sudo -u www-data /path/to/venv/bin/python -c "import django; print(django.get_version())"to confirm. - Restart everything. After making changes, restart Gunicorn and your web server (Nginx/Apache) to apply the changes:
sudo systemctl restart gunicornandsudo systemctl restart nginx.
Common Mistakes
- Running Gunicorn as root but the socket is owned by another user.
- Forgetting to set execute permission on parent directories (e.g.,
/home/user/project– if/home/useris 700, others can’t traverse). - Using a socket path in a directory that doesn’t exist.
- Mixing up the user in the systemd service file with the one that owns the files.
- Ignoring SELinux – it can cause mysterious permission denials even when file permissions look correct.
Tips
- Use a dedicated system user for Gunicorn (e.g.,
www-dataorgunicorn) to minimize security risks. - Keep static and media files in a separate directory with appropriate permissions, and let Nginx serve them directly.
- Use
--bind 127.0.0.1:8000for testing to avoid socket issues during development. - Document your file ownership and permission scheme in your project README.
- Use
sudo -u <user> -ito simulate the Gunicorn user’s environment and test commands.
Troubleshooting
If the error persists after following the steps:
- Check logs. Look at Gunicorn’s error log (often in
/var/log/gunicorn/) and the web server’s error log. The exact line number and file path will guide you. - Run Gunicorn in the foreground as the target user. Use
sudo -u www-data /path/to/venv/bin/gunicorn --bind 127.0.0.1:8000 myproject.wsgito see the error directly. - Use
straceto trace system calls. If you have root access, runstrace -f -e trace=file -p <gunicorn_pid>to see exactly which file is denied. - Check if the socket is already in use. If the socket file exists but is stale, remove it:
sudo rm /run/gunicorn/myproject.sockand restart. - Verify Nginx proxy_pass. If Nginx is proxying to Gunicorn, ensure the
proxy_passURL matches the socket or port Gunicorn is bound to.
Frequently Asked Questions
Why does Gunicorn get permission denied even though I set chmod 777?
chmod 777 gives read/write/execute to everyone, but if the parent directory lacks execute permission, the user still can’t traverse to the file. Also, SELinux can override file permissions.
Should I run Gunicorn as root?
No. Running as root is a security risk. Use a dedicated user with minimal privileges.
How do I fix permission denied when binding to a port below 1024?
Ports below 1024 require root privileges. Use a port above 1024 (e.g., 8000) and let Nginx or Apache handle the low port.
What if my project is in a directory owned by another user?
You can either change ownership with chown or add the Gunicorn user to the group that owns the directory and set group permissions.
Can I use a TCP socket instead of a Unix socket to avoid permission issues?
Yes, binding to 127.0.0.1:8000 avoids file permission issues, but you must ensure the port is not blocked by a firewall.
Why does Gunicorn work in development but fail in production?
Often the production environment uses a different user, stricter SELinux, or a systemd service that doesn’t set the correct working directory.
Related
Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.
More cards in this drawer
- Philips DST7060/20 7000 Series Iron: Specs & Fit
- Coal Fueled Jet Aircraft: Could It Work?675 L
- Remote Desktop on Raspberry Pi: How to Enable It
- Word Select Last Line in Paragraph?
- Google Assistant IFTTT Recipes: 7 Useful Automations
- OLED TVs in Dark Rooms: Why They Look Better and When They Don’t