Docker에서 PHP 파일 업로드 설정 방법 | php.ini · Nginx · docker-compose
Docker로 구축한 환경에서는 "로컬에서 설정했는데 컨테이너에 반영되지 않음" 또는 "Nginx 컨테이너를 수정했지만 PHP-FPM 컨테이너 설정이 남아있음"과 같은 문제가 자주 발생합니다. 이는 Docker 컨테이너가 독립된 환경을 가지고 있어서 설정 파일이 호스트와 컨테이너 간에 분리되어 있기 때문입니다. 본 글에서는 Nginx 컨테이너와 PHP-FPM 컨테이너의 조합으로 파일 업로드 설정을 올바르게 수행하는 방법을 체계적으로 설명합니다.
Docker에서의 PHP 커스텀 설정 방법
PHP 컨테이너에 사용자 정의 php.ini 설정을 적용하는 방법은 주로 두 가지입니다: Dockerfile에서 이미지 빌드 시 파일을 복사하는 방법과 docker-compose에서 볼륨 마운트를 사용하는 방법입니다.
방법 1: Dockerfile에서 <code>php.ini</code> 복사
# Dockerfile(PHP-FPM コンテナ)
FROM php:8.3-fpm-alpine
# 必要な拡張をインストール
RUN docker-php-ext-install pdo_mysql opcache
# カスタム php.ini をコンテナにコピー
# php.ini-development または php.ini-production をベースにする
COPY ./docker/php/php.ini /usr/local/etc/php/php.ini
# または conf.d ディレクトリに追加設定ファイルとして置く(推奨)
# 既存設定を上書きせず、変更したい値だけ記述できる
COPY ./docker/php/custom.ini /usr/local/etc/php/conf.d/99-custom.ini
WORKDIR /var/www/html
방법 2: docker-compose에서 볼륨 마운트
# docker-compose.yml
services:
php:
build:
context: .
dockerfile: ./docker/php/Dockerfile
volumes:
# アプリのソースコード
- ./src:/var/www/html
# php.ini をマウント(ファイル変更が即反映される)
- ./docker/php/custom.ini:/usr/local/etc/php/conf.d/99-custom.ini:ro
볼륨 마운트 방식은 개발 환경에 적합하며 설정 파일을 수정하고 컨테이너를 재시작하기만 하면 반영됩니다. 프로덕션 환경에서는 이미지에 복사하는 Dockerfile 방식이 권장됩니다(이미지의 재현성을 유지하기 위해).
필요한 설정값과 php.ini의 작성 방법
파일 업로드와 관련된 설정값은 4가지입니다. 이들을 함께 설정한 <code>custom.ini</code> 파일을 준비합니다.
; docker/php/custom.ini
; ============================================
; ファイルアップロード関連の設定
; ============================================
; ファイルアップロードを有効化(デフォルトOnだが明示する)
file_uploads = On
; 1ファイルあたりのアップロード上限
; Nginx の client_max_body_size と合わせること
upload_max_filesize = 100M
; POSTリクエスト全体の上限
; upload_max_filesize より大きくすること(フォームフィールドのオーバーヘッド分)
post_max_size = 110M
; ============================================
; メモリ・実行時間の設定
; ============================================
; PHPスクリプトが使用できる最大メモリ
; post_max_size より大きくすること
memory_limit = 256M
; スクリプトの最大実行時間(秒)
; 大きなファイルのアップロードや処理に対応
max_execution_time = 300
; リクエストデータ(POSTやファイル)の読み込み最大時間(秒)
; -1 で max_execution_time に従う
max_input_time = 300
php.ini 설정 파일의 위치를 확인하는 방법
컨테이너 내에서 어떤 php.ini가 로드되고 있는지 확인하려면 다음 명령을 사용합니다.
# コンテナ内で実行(docker exec 経由)
docker exec -it <コンテナ名> php -i | grep "php.ini"
# 出力例:
# Configuration File (php.ini) Path => /usr/local/etc/php
# Loaded Configuration File => /usr/local/etc/php/php.ini
# Additional .ini files parsed(conf.d の追加設定)
docker exec -it <コンテナ名> php -i | grep "additional"
# Additional .ini files parsed => /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini,
# /usr/local/etc/php/conf.d/99-custom.ini
# 特定の設定値を確認
docker exec -it <コンテナ名> php -i | grep -E "upload_max|post_max|memory_limit"
# upload_max_filesize => 100M
# post_max_size => 110M
# memory_limit => 256M
웹을 통해 확인하는 경우 <code>phpinfo()</code>를 사용합니다.
<?php
// /var/www/html/public/info.php (一時的なデバッグ用)
phpinfo();
// ※ 本番環境では削除すること(サーバー情報が丸見えになる)
Nginx 컨테이너와 PHP-FPM 컨테이너 간의 설정 관계
Docker의 일반적인 PHP 구성은 Nginx 컨테이너와 PHP-FPM 컨테이너를 분리하는 2개 컨테이너 아키텍처입니다. 각각 독립적인 제한을 가지므로 <strong>둘 다 적절히 설정되지 않으면 업로드가 실패합니다</strong>.
クライアント
│
▼(HTTP リクエスト)
┌──────────────────────────────┐
│ Nginx コンテナ │ ← client_max_body_size で制限
│ client_max_body_size 100m; │ ここを超えると 413 エラー
└──────────────────────────────┘
│
▼(FastCGI プロトコル)
┌──────────────────────────────┐
│ PHP-FPM コンテナ │ ← upload_max_filesize / post_max_size で制限
│ upload_max_filesize = 100M │ ここを超えると UPLOAD_ERR_INI_SIZE
│ post_max_size = 110M │
└──────────────────────────────┘
# docker/nginx/conf.d/default.conf
server {
listen 80;
server_name localhost;
root /var/www/html/public;
index index.php;
# Nginx レベルのアップロード上限
# PHP の upload_max_filesize / post_max_size 以上にする
client_max_body_size 110m;
# タイムアウト設定(大容量ファイル処理用)
proxy_read_timeout 300s;
proxy_send_timeout 300s;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass php:9000; # PHP-FPMコンテナのサービス名:ポート
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
# FastCGI のタイムアウト設定
fastcgi_read_timeout 300s;
fastcgi_send_timeout 300s;
}
}
docker-compose.yml의 완전한 설정 예
Nginx + PHP-FPM + 볼륨 마운트를 포함한 완전한 docker-compose.yml의 예를 보여줍니다.
version: '3.8'
services:
# Nginx コンテナ
nginx:
image: nginx:1.27-alpine
ports:
- "8080:80"
volumes:
# アプリのソースコード(Nginx から静的ファイルを配信)
- ./src:/var/www/html/public:ro
# Nginx 設定ファイル
- ./docker/nginx/conf.d:/etc/nginx/conf.d:ro
depends_on:
- php
networks:
- app-network
# PHP-FPM コンテナ
php:
build:
context: .
dockerfile: ./docker/php/Dockerfile
volumes:
# アプリのソースコード
- ./src:/var/www/html
# PHP カスタム設定(開発時はマウントで即反映)
- ./docker/php/custom.ini:/usr/local/etc/php/conf.d/99-custom.ini:ro
# アップロードファイルの一時保存先(権限に注意)
- upload_tmp:/tmp/php-uploads
environment:
PHP_UPLOAD_TMP_DIR: /tmp/php-uploads
networks:
- app-network
networks:
app-network:
driver: bridge
volumes:
upload_tmp:
driver: local
# docker/php/Dockerfile
FROM php:8.3-fpm-alpine
# 必要なシステムパッケージ
RUN apk add --no-cache \
libpng-dev \
libjpeg-turbo-dev \
libwebp-dev \
freetype-dev
# PHP 拡張のインストール
RUN docker-php-ext-configure gd \
--with-freetype \
--with-jpeg \
--with-webp && \
docker-php-ext-install \
gd \
pdo_mysql \
opcache \
exif
# アプリユーザーを作成(www-data と権限を合わせる)
RUN addgroup -g 1000 appuser && \
adduser -u 1000 -G appuser -s /bin/sh -D appuser
USER appuser
WORKDIR /var/www/html
설정이 반영되었는지 확인하는 방법
설정을 변경한 후에는 항상 컨테이너를 다시 시작하고 설정이 올바르게 로드되었는지 확인합니다.
# コンテナの再起動(設定ファイル変更を反映)
docker compose restart php
# または特定の設定値だけ確認
docker compose exec php php -r "echo ini_get('upload_max_filesize'), PHP_EOL;"
docker compose exec php php -r "echo ini_get('post_max_size'), PHP_EOL;"
docker compose exec php php -r "echo ini_get('memory_limit'), PHP_EOL;"
# 複数の値をまとめて確認
docker compose exec php php -r "
foreach (['upload_max_filesize', 'post_max_size', 'memory_limit', 'max_execution_time'] as \$key) {
echo \$key . ' = ' . ini_get(\$key) . PHP_EOL;
}"
# Nginx の設定確認
docker compose exec nginx nginx -T | grep -E "client_max_body|server_name"
# Nginx の設定をリロード(コンテナ再起動なし)
docker compose exec nginx nginx -s reload
일반적인 문제와 해결방법
| 증상 | 원인 | 해결책 |
|---|---|---|
| 413 Request Entity Too Large | Nginx <code>client_max_body_size</code> 초과 | Nginx 설정을 높이고 <code>nginx -s reload</code> 실행 |
| PHP 레벨에서 UPLOAD_ERR_INI_SIZE | <code>upload_max_filesize</code> 초과 | custom.ini를 수정하고 컨테이너 재시작 |
| 설정을 변경해도 반영되지 않음 | 컨테이너 재시작 필요 / 설정 파일의 마운트 경로가 잘못됨 | <code>docker compose restart php</code> / 경로 확인 |
| 임시 디렉토리 쓰기 실패 | PHP 컨테이너의 사용자 권한과 <code>/tmp</code> 권한 불일치 | 볼륨 권한 설정 및 <code>upload_tmp_dir</code> 확인 |
| 대용량 파일에서의 타임아웃 | <code>fastcgi_read_timeout</code> 또는 <code>max_execution_time</code>이 너무 짧음 | 두 값 모두 올리기 |
프로덕션 환경을 위한 추가 사항
프로덕션 환경에서는 다음 사항도 고려하세요.
- php.ini 는 이미지 빌드 시 <code>COPY</code> 로 포함시키고 볼륨으로 마운트하지 않습니다(재현성을 위해)
- <code>display_errors = Off</code>・<code>log_errors = On</code>을 반드시 설정하기
- 업로드 대상이 S3 같은 클라우드 스토리지인 경우, 컨테이너 로컬에 대한 임시 저장은 요청 처리 중에만 유지하세요.
- 다중 레플리카(수평 확장) 구성에서는 로컬 업로드 대상을 사용할 수 없습니다. 대신 S3 또는 공유 스토리지(EFS, NFS)를 사용하세요.
- PHP-FPM의 프로세스 수 (<code>pm.max_children</code>)도 대용량 업로드가 동시에 많이 발생하면 메모리를 압박합니다
이 기사에서 사용할 수 있는 테스트 파일 (무료)
- → <a href="/ja/files/threshold/" class="text-primary-600 dark:text-primary-400 hover:underline">경계값 테스트 파일 목록</a> — upload_max_filesize 설정값 전후에서 오류 재현 및 확인
- → <a href="/ja/files/images/" class="text-primary-600 dark:text-primary-400 hover:underline">테스트 이미지 목록</a> — Docker 환경에서 다양한 크기의 업로드 동작 검증
관련 기사
- → <a href="/ja/blog/nginx-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">Nginx client_max_body_size를 올바르게 설정하는 방법 | 업로드 제한 문제 해결</a>
- → <a href="/ja/blog/php-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">PHP에서 파일 업로드를 구현하는 방법 | 검증・저장・보안 완벽 가이드</a>
- → <a href="/ja/blog/laravel-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">Laravel 파일 업로드 구현 가이드|검증·Storage·S3 대응</a>