777权限不是解法而是隐患源头;真正冲突在于www-data等运行用户与部署用户的归属不一致,SQLite文件、media/、static/三处最易出问题,首要确认“谁在跑进程,谁拥有文件”。

直接给结论:777 权限不是解法,而是隐患源头;真正的冲突往往发生在 www-data(或 nginx、gunicorn 用户)与你的部署用户之间,且 SQLite 数据库文件、media/ 目录、static/ 收集目录三处最易出问题。
确认 Django 运行用户和文件所有者是否匹配
权限冲突的第一环,永远是“谁在跑进程,谁拥有文件”。Django 本身不决定用户,Web 服务器(如 Nginx + Gunicorn)或 WSGI 容器才决定实际执行身份。
- 用
ps aux | grep gunicorn或ps aux | grep uwsgi查看主进程的 USER 列,常见为www-data、nginx或自定义用户(如myapp) - 用
ls -ld /path/to/your/project和ls -l /path/to/your/project/db.sqlite3对比所有者(OWNER)是否一致 - 若不一致,不要改用户去迁就文件,而应统一归到运行用户下:
sudo chown -R www-data:www-data /path/to/your/project - 特别注意:如果用了虚拟环境,
venv/目录也必须对运行用户可读(但不可写),否则导入模块会失败
SQLite 数据库文件和目录必须同时可写
django.db.utils.OperationalError: attempt to write a readonly database 看似只怪 .db 文件,实则常因上层目录无写权限被静默拦截。
- 检查数据库文件权限:
ls -l /path/to/db.sqlite3—— 至少需要-rw-rw----(660)或-rw-r--r--(644),且所有者必须是运行用户 - 检查数据库所在目录权限:
ls -ld /path/to/—— 必须含w(写),典型安全值是drwxrws---(770),组权限带s(setgid)确保新文件继承组 - 避免用
chmod 777:它让任何本地用户都能删库,且 SELinux/AppArmor 可能直接拒绝 777 目录下的写操作 - 如果数据库放在
/tmp下测试通过,但放项目内就失败——基本可断定是目录权限没开,而非文件本身
media/ 和 static/ 目录的权限策略要区分对待
media/ 是用户上传入口,static/ 是收集后只读资源,二者权限逻辑完全不同。
立即学习“Python免费学习笔记(深入)”;
-
media/目录必须对运行用户可写可执行(drwxrws---770),且需设 setgid:sudo chmod g+s media/,否则上传文件的组可能变成www-data而非预期组,导致后续管理脚本无法处理 -
static/目录只需运行用户可读(drwxr-x---750),收集命令python manage.py collectstatic应由部署用户执行,再chown给运行用户,而不是让运行用户自己去写 static/ - 别把
media/挂在 Web 根目录下却不加访问控制——Nginx 需显式禁止执行上传的 .py/.sh 文件,例如:location ~* \.(py|sh|bash)$ { deny all; }
避免 umask 和 systemd 服务配置引发的隐性权限问题
很多权限问题不报错,但新建文件默认不可写,根源在进程启动时的 umask 值或 systemd 服务单元配置。
- Gunicorn/uWSGI 若以 systemd 服务运行,检查
UMask=字段,默认可能是0022(创建文件为 644,目录为 755),这会导致上传文件只有运行用户可写,组和其他人不可写——需改为UMask=0002 - 在服务文件(如
/etc/systemd/system/gunicorn.service)中加入:UMask=0002和PermissionsStartOnly=true,后者允许ExecStartPre阶段用 root 改权限 - 如果用 supervisor,对应配置项是
umask=0002,漏配就会让media/下新文件权限为 644,组无写权 - 开发机上测试正常,上线就失败?大概率是生产环境 shell 的
umask(查umask命令输出)和 systemd 的UMask不一致
最常被忽略的一点:权限问题从来不是孤立的。当你 fix 掉一个 PermissionError,务必顺手检查 ls -l 输出里那个 .(当前目录)、..(父目录)、db.sqlite3、media/ 四者的 owner/group 是否全部对齐,以及父目录是否含执行位(x)——缺任何一个,Linux 就会拦住路径遍历,报错却只说“permission denied”。


















