You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Sail无法创建新日志文件:权限拒绝问题求解

解决Laravel Sail中日志文件权限拒绝问题

问题背景

使用Laravel Sail时,Laravel创建新日志文件时触发权限错误:

The stream or file "/var/www/html/storage/logs/laravel-2022-11-05.log" could not be opened in append mode: Failed to open stream: Permission denied

当前通过手动执行chmod -R 777 storage/logs临时修复,但希望通过Docker配置实现永久解决。

提供的Dockerfile和start-container脚本内容如下:

Dockerfile

FROM ubuntu:22.04

LABEL maintainer="Taylor Otwell"

ARG WWWGROUP
ARG NODE_VERSION=16
ARG POSTGRES_VERSION=14

WORKDIR /var/www/html

ENV DEBIAN_FRONTEND noninteractive
ENV TZ=UTC

RUN apt-get update \
    && apt-get install -y cron

RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

RUN apt-get update \
    && apt-get install -y gnupg gosu curl ca-certificates zip unzip git supervisor sqlite3 libcap2-bin libpng-dev python2 \
    && mkdir -p ~/.gnupg \
    && chmod 600 ~/.gnupg \
    && echo "disable-ipv6" >> ~/.gnupg/dirmngr.conf \
    && echo "keyserver hkp://keyserver.ubuntu.com:80" >> ~/.gnupg/dirmngr.conf \
    && gpg --recv-key 0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c \
    && gpg --export 0x14aa40ec0831756756d7f66c4f4ea0aae5267a6c > /usr/share/keyrings/ppa_ondrej_php.gpg \
    && echo "deb [signed-by=/usr/share/keyrings/ppa_ondrej_php.gpg] https://ppa.launchpadcontent.net/ondrej/php/ubuntu jammy main" > /etc/apt/sources.list.d/ppa_ondrej_php.list \
    && apt-get update \
    && apt-get install -y php8.1-cli php8.1-dev \
       php8.1-pgsql php8.1-sqlite3 php8.1-gd \
       php8.1-curl \
       php8.1-imap php8.1-mysql php8.1-mbstring \
       php8.1-xml php8.1-zip php8.1-bcmath php8.1-soap \
       php8.1-intl php8.1-readline \
       php8.1-ldap \
       php8.1-msgpack php8.1-igbinary php8.1-redis php8.1-swoole \
       php8.1-memcached php8.1-pcov php8.1-xdebug \
    && php -r "readfile('https://getcomposer.org/installer');" | php -- --install-dir=/usr/bin/ --filename=composer \
    && curl -sLS https://deb.nodesource.com/setup_$NODE_VERSION.x | bash - \
    && apt-get install -y nodejs \
    && npm install -g npm \
    && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarn.gpg >/dev/null \
    && echo "deb [signed-by=/usr/share/keyrings/yarn.gpg] https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list \
    && curl -sS https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/pgdg.gpg >/dev/null \
    && echo "deb [signed-by=/usr/share/keyrings/pgdg.gpg] http://apt.postgresql.org/pub/repos/apt jammy-pgdg main" > /etc/apt/sources.list.d/pgdg.list \
    && apt-get update \
    && apt-get install -y yarn \
    && apt-get install -y mysql-client \
    && apt-get install -y postgresql-client-$POSTGRES_VERSION \
    && apt-get -y autoremove \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.1

RUN groupadd --force -g 1000 sailRoot
RUN useradd -ms /bin/bash --no-user-group -g 1000 -u 1337 sailRoot

RUN usermod -aG sudo sail
RUN usermod -aG sudo sailRoot

COPY scheduler /etc/cron.d/scheduler
RUN chmod 0644 /etc/cron.d/scheduler \
    && crontab /etc/cron.d/scheduler

COPY start-container /usr/local/bin/start-container
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY php.ini /etc/php/8.1/cli/conf.d/99-sail.ini
RUN chmod +x /usr/local/bin/start-container

RUN chown -R www-data:www-data /var/www

EXPOSE 8000

ENTRYPOINT ["start-container"]

start-container脚本

#!/usr/bin/env bash

if [ ! -z "1000" ]; then
    usermod -u 1000 sail
fi

if [ ! -d /.composer ]; then
    mkdir /.composer
fi

chmod -R ugo+rw /.composer

if [ $# -gt 0 ]; then
    exec gosu 1000 "$@"
else
    exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
fi

set -xe
cd /var/www/html
chmod -R 755 app
chmod -R 755 public
chmod -R 755 config
chmod -R 755 storage
chmod -R 777 storage/logs
chmod -R 755 database
chmod -R 755 bootstrap
chmod -R 777 resources
chmod -R 755 routes
chmod -R 755 composer.json
chmod -R 755 composer.lock

解决方案

1. 修复脚本执行顺序问题

当前脚本中权限修改代码位于exec命令之后,而exec会替换当前进程,导致权限调整代码永远不会执行。将权限调整逻辑移到exec之前:

修改后的start-container脚本:

#!/usr/bin/env bash

if [ ! -z "1000" ]; then
    usermod -u 1000 sail
fi

if [ ! -d /.composer ]; then
    mkdir /.composer
fi

chmod -R ugo+rw /.composer

# 先执行权限调整
set -xe
cd /var/www/html
chown -R www-data:www-data storage bootstrap/cache
chmod -R 775 storage bootstrap/cache
chmod -R 777 storage/logs

# 再执行主命令
if [ $# -gt 0 ]; then
    exec gosu www-data "$@"
else
    exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
fi

2. 同步宿主与容器用户UID

如果宿主机器的用户UID(通常为1000)与容器内用户UID不匹配,会导致挂载目录权限冲突。通过ARG和环境变量同步UID:

Dockerfile修改

ARG HOST_UID=1000
RUN groupadd --force -g ${WWWGROUP:-1000} sailRoot
RUN useradd -ms /bin/bash --no-user-group -g ${WWWGROUP:-1000} -u ${HOST_UID} sailRoot

start-container脚本修改

if [ ! -z "$HOST_UID" ]; then
    usermod -u "$HOST_UID" sailRoot
fi

docker-compose.yml补充

services:
    laravel.test:
        build:
            args:
                WWWGROUP: ${WWWGROUP:-1000}
                HOST_UID: ${UID:-1000}
        environment:
            - HOST_UID=${UID:-1000}

3. 配置容器以www-data用户运行

将容器内执行Laravel服务的用户改为www-data,确保与storage目录归属一致:

  • 修改start-container脚本中的exec gosu 1000 "$@"为exec gosu www-data "$@"
  • 在Dockerfile中明确设置关键目录归属:
RUN chown -R www-data:www-data /var/www/html/storage /var/www/html/bootstrap/cache
  • 同步修改supervisord.conf中Laravel服务的运行用户为www-data

内容的提问来源于stack exchange,提问作者PHP User

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 13:20:30