Synology Docker 에서는 UID 정렬(1000) 방식이 최선이다.
RUN groupmod -g 1000 www-data \ && usermod -u 1000 -g 1000 www-data
“artisan은 항상 Laravel을 실제 실행하는 사용자(www-data)로 실행한다.”
Permission denied 오류의 원인과 해결 과정
Synology NAS에서 Docker를 이용해 Laravel을 운영하다 보면, 가장 자주 마주치는 오류 중 하나가 바로 다음과 같은 메시지입니다.
The stream or file "/app/storage/logs/laravel.log" could not be opened in append mode: Permission denied
The /app/bootstrap/cache directory must be present and writable.
이 글에서는 실제로 발생했던 문제를 바탕으로, 왜 이런 오류가 발생하는지, 어떻게 해결해야 하는지, 그리고 앞으로 같은 문제가 재발하지 않도록 어떤 방식으로 환경을 구성해야 하는지를 단계적으로 정리합니다.
1. 문제 상황
Synology 환경에서 Docker Compose를 이용해 Laravel을 운영 중이었고, 구성은 대략 다음과 같았습니다.
- Nginx 컨테이너
- PHP-FPM 컨테이너(app)
- Queue Worker 컨테이너
Laravel 프로젝트 폴더는 Synology의 다음 경로에 위치했습니다.
/volume1/docker/admin-ppn
그런데 어느 순간부터 Laravel 로그 기록과 캐시 파일 생성이 되지 않으면서 위와 같은 Permission denied 오류가 반복적으로 발생했습니다.
2. 원인 분석
문제의 핵심은 두 가지였습니다.
(1) Synology 파일 권한 구조
Synology NAS에서 파일을 생성하면, 기본적으로 DSM 사용자의 UID/GID가 적용됩니다. 대부분의 경우 이 값은 다음과 같습니다.
UID: 1000
GID: 1000
즉, 다음 경로들은 실제로 이런 소유권을 가지고 있었습니다.
/app/storage → 1000:1000
/app/bootstrap/cache → 1000:1000
(2) 컨테이너 내부 PHP-FPM 사용자
반면, Docker 공식 PHP-FPM 이미지는 기본적으로 다음 사용자로 동작합니다.
www-data (UID 33)
실제로 컨테이너 안에서 확인해 보면 다음과 같이 나왔습니다.
uid=33(www-data) gid=33(www-data)
3. 권한 충돌의 발생
이 두 환경이 충돌하면서 문제가 발생했습니다.
- Laravel은
storage,bootstrap/cache폴더에 쓰기 권한이 필요 - 해당 폴더는 1000:1000 소유
- PHP 프로세스는 33:33 사용자로 실행
권한 구조는 보통 775 형태이므로 다음 규칙이 적용됩니다.
- 소유자(1000) → 쓰기 가능
- 그룹(1000) → 쓰기 가능
- 기타 사용자 → 쓰기 불가
결과적으로 www-data(33)는 해당 폴더에 쓸 수 없었고, Laravel은 지속적으로 Permission denied 오류를 발생시킨 것입니다.
4. 잘못된 해결 방식
많은 자료에서 다음과 같은 해결 방법을 제시합니다.
chmod -R 777 storage bootstrap/cache
하지만 이 방식은 보안 관점에서도 좋지 않고, 근본 원인을 해결하지 못합니다.
또 다른 방식은 호스트에서 다음과 같이 소유권을 변경하는 방법입니다.
chown -R 33:33 storage bootstrap/cache
이 방법은 일시적으로 동작하지만, Synology 파일 관리 관점에서 불편함을 유발하고 장기적으로 권장되지 않습니다.
5. 근본 해결책
가장 안정적인 해결책은 다음과 같습니다.
컨테이너 내부 www-data 사용자의 UID를 Synology와 맞추기
Synology 파일 소유권이 1000:1000이라면, PHP-FPM 컨테이너 내부의 www-data 사용자도 동일하게 1000으로 맞추는 것이 가장 자연스럽습니다.
이를 위해 Dockerfile에 다음 내용을 추가합니다.
RUN groupmod -g 1000 www-data \
&& usermod -u 1000 -g 1000 www-data
이렇게 하면 다음과 같은 구조가 됩니다.
| 구분 | UID/GID |
|---|---|
| Synology 파일 | 1000:1000 |
| PHP-FPM 프로세스 | 1000:1000 |
두 환경이 완전히 일치하므로 권한 충돌이 사라집니다.
6. 실제로 겪은 시행착오
위 설정을 Dockerfile에 추가했음에도 불구하고, 오류는 계속 발생했습니다. 원인은 다음과 같았습니다.
- Dockerfile은 수정되었지만
- 이미 빌드된 예전 이미지(UID 33 기준)가 계속 사용되고 있었음
- Docker 빌드 캐시 때문에 변경 사항이 반영되지 않음
즉, 코드 수정은 되었지만 실제 컨테이너는 예전 상태로 실행되고 있었던 것입니다.
7. 최종 해결 과정
다음 명령을 통해 문제를 완전히 해결할 수 있었습니다.
docker compose build --no-cache app
docker compose up -d
이 명령은 Docker 빌드 캐시를 완전히 무시하고 이미지를 다시 생성합니다.
재빌드 후 컨테이너 내부에서 확인한 결과는 다음과 같았습니다.
uid=1000(www-data) gid=1000(www-data)
이 순간부터 Permission denied 오류는 완전히 사라졌습니다.
8. Compose 구성의 개선
추가적으로 권장되는 구조 개선 사항도 정리합니다.
(1) 겹쳐 마운트 제거
기존에는 다음과 같은 형태로 마운트되어 있었습니다.
/app/app/storage/app/bootstrap/cache
이 방식은 권한 문제를 더 복잡하게 만들 수 있습니다.
보다 안정적인 방식은 다음과 같습니다.
- PHP-FPM 컨테이너는
/app전체만 마운트 - Nginx 컨테이너는
/app/public만 read-only로 마운트
이 구조가 권한 충돌 가능성을 가장 최소화합니다.
9. 운영 규칙 정리
이번 경험을 통해 정리한 권장 운영 규칙입니다.
(1) Dockerfile 규칙
- 반드시 www-data UID를 1000으로 정렬
- PHP-FPM listen은
0.0.0.0:9000으로 설정
(2) 권한 관리 규칙
- storage와 bootstrap/cache는 항상 1000:1000 유지
- DSM에서 직접 파일을 생성하지 않음
- artisan 명령은 반드시 www-data로 실행
예시:
docker exec -u www-data -it admin-ppn-app php artisan migrate
(3) 빌드 규칙
- Dockerfile 변경 시 반드시 no-cache 빌드 수행
- queue-worker도 동일 이미지로 재빌드
10. 결론
Synology + Docker + Laravel 환경에서 Permission denied 오류는 거의 대부분 다음 이유로 발생합니다.
- 호스트 파일 UID(1000)와 컨테이너 사용자 UID(33)의 불일치
- Docker 빌드 캐시로 인한 설정 미반영
- 겹쳐 마운트 구조로 인한 권한 혼선
이 세 가지만 정확히 이해하고 정리하면, 권한 문제는 완전히 해결할 수 있습니다.
마무리
이번 과정을 통해 단순히 오류를 해결하는 것을 넘어, Synology와 Docker 환경에서 Laravel을 안정적으로 운영하기 위한 구조적 기준을 정립할 수 있었습니다. 비슷한 환경을 운영 중인 분들에게 이 글이 실질적인 도움이 되기를 바랍니다.
추가사항
Synology Container Manager의 “터미널 열기”에서 php artisan ...을 실행하면, 그 명령은 기본적으로 “컨테이너의 기본 사용자” 권한으로 실행됩니다. 그리고 그 기본 사용자는 대부분 root입니다(특히 공식 PHP 이미지나 USER를 지정하지 않은 커스텀 이미지에서 그렇습니다).
즉, 별도 조치를 하지 않으면 다음 상황이 됩니다.
- 터미널에서 실행한 php artisan ... → root 권한으로 실행
- 그 결과 storage/, bootstrap/cache/, storage/framework/* 등에 root 소유 파일이 생성될 수 있음
- 이후 PHP-FPM(보통 www-data)이 그 파일을 못 건드려서 Permission denied가 재발
1) “터미널 열기”에서 실제 권한 확인하는 방법
whoami
id
- root라고 나오면, 그 터미널에서 실행한 artisan은 root로 실행됩니다.
- www-data라고 나오면, www-data로 실행됩니다.
2) 그럼 Container Manager 터미널에서 artisan을 안전하게 실행하려면?
root로 접속된 터미널에서도, artisan 실행만 www-data로 내리면 됩니다.
가장 확실한 방식(권장)
su -s /bin/sh www-data -c "php artisan migrate --force"
su -s /bin/sh www-data -c "php artisan config:cache"
- 터미널 자체는 root여도
- 해당 명령만 www-data로 실행되므로
- storage/cache에 생성되는 파일이 www-data(또는 UID 정렬된 사용자) 소유로 만들어집니다.
3) UID 정렬(1000) 방식이라면 권한은 어떻게 되나?
당신이 Dockerfile에서 www-data를 UID 1000으로 맞춘 상태라면:
- su -s /bin/sh www-data -c "php artisan migrate --force" 로 실행했을 때
- 생성되는 파일은 UID 1000 소유로 만들어집니다.
- Synology 폴더 소유권(1000:1000)과 일치하므로 가장 안정적입니다.
반대로, 터미널에서 root로 artisan을 실행하면:
- root 소유 파일이 생기고
- 다시 권한 오류를 부르는 패턴이 반복될 수 있습니다.
UID 정렬을 이미 적용한 상태에서는 다음이 가능합니다.
| 실행 위치 | 실행 방식 | 결과 |
| Synology SSH | docker exec 기본 | 대부분 문제 없음 |
| Synology SSH | docker exec -u www-data | 가장 안전 |
| Container Manager 터미널 | root로 artisan | 대체로 문제 없음 |
| Container Manager 터미널 | su www-data | 권장 |
'Synology 시놀로지' 카테고리의 다른 글
| Synology Web Station 에서 Laravel 프로젝트가 실행 가능한가 ? (0) | 2026.01.11 |
|---|---|
| Synology Docker로 Next.js 실행하기 (리버스 프록시) (0) | 2025.12.18 |
| 2개의 Synology 서버 공유폴더 동기화 > 단방향 (0) | 2025.04.09 |
| Synology rsync란? > 백업하기 (0) | 2025.04.09 |
| Synology NAS 백엔드서버 구축하기 (1) | 2025.04.07 |