How to Configure PHP File Upload Settings in Docker | php.ini, Nginx, docker-compose
In Docker environments, issues like "configuration applied locally but not reflected in the container" or "fixed Nginx container but PHP-FPM container settings remain" are common. This is because Docker containers have isolated environments where configuration files are separated between host and container. This article systematically explains how to properly configure file uploads with Nginx and PHP-FPM containers.
PHP Custom Configuration Methods in Docker
There are mainly two ways to apply custom php.ini settings to a PHP container: copying the file during image build in Dockerfile, or using volume mount in docker-compose.
Method 1: Copy <code>php.ini</code> in Dockerfile
# 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
Method 2: Volume mount with 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
Volume mounting is suitable for development environments and can be applied by simply modifying the configuration file and restarting the container. For production environments, the Dockerfile method of copying to the image is recommended (to maintain image reproducibility).
Required configuration values and how to write <code>php.ini</code>
There are 4 configuration values related to file uploads. We will prepare a <code>custom.ini</code> file with these settings consolidated.
; 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
How to find the location of the php.ini configuration file
To check which <code>php.ini</code> is being loaded inside the container, use the following command.
# コンテナ内で実行(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
To check via the web, use <code>phpinfo()</code>.
<?php
// /var/www/html/public/info.php (一時的なデバッグ用)
phpinfo();
// ※ 本番環境では削除すること(サーバー情報が丸見えになる)
Configuration relationship between Nginx and PHP-FPM containers
A typical Docker PHP setup is a 2-container architecture separating Nginx and PHP-FPM containers. Since each has independent limits, <strong>uploads will fail if both are not properly configured</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;
}
}
Complete Configuration Example of docker-compose.yml
Here is a complete example of a docker-compose.yml that includes Nginx + PHP-FPM + volume mount.
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
How to verify if settings are applied
After changing settings, always restart the container and verify that the configuration is being loaded correctly.
# コンテナの再起動(設定ファイル変更を反映)
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
Common problems and solutions
| Symptoms | Cause | Solution |
|---|---|---|
| 413 Request Entity Too Large | Nginx <code>client_max_body_size</code> Exceeded | Increase Nginx configuration and run <code>nginx -s reload</code> |
| UPLOAD_ERR_INI_SIZE at PHP level | <code>upload_max_filesize</code> exceeded | Modify custom.ini and Restart Container |
| Settings do not apply even after changing them | Container restart required / Incorrect mount path for configuration file | <code>docker compose restart php</code> / Check the path |
| Write to temporary directory failed | Mismatch between PHP container user permissions and <code>/tmp</code> permissions | Check volume permission settings and <code>upload_tmp_dir</code> |
| Timeout with large files | <code>fastcgi_read_timeout</code> or <code>max_execution_time</code> is too short | Increase both values |
Production environment notes
Also consider the following points in the production environment.
- Embed php.ini with <code>COPY</code> during image build and do not mount as a volume (for reproducibility)
- Always set <code>display_errors = Off</code> and <code>log_errors = On</code>
- When the upload destination is cloud storage like S3, keep temporary storage in the container local to the duration of request processing only.
- In a multi-replica (horizontal scale) configuration, you cannot use local upload destinations. Use S3 or shared storage (EFS, NFS) instead.
- The number of PHP-FPM processes (<code>pm.max_children</code>) can also strain memory when many large uploads occur simultaneously
Test files for this article (free)
- → <a href="/ja/files/threshold/" class="text-primary-600 dark:text-primary-400 hover:underline">Boundary Value Test Files</a> — Reproduce and verify errors around the upload_max_filesize setting
- → <a href="/ja/files/images/" class="text-primary-600 dark:text-primary-400 hover:underline">Test Images List</a> — Verify upload behavior in Docker environment across various sizes
Related articles
- → <a href="/ja/blog/nginx-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">How to Properly Configure Nginx client_max_body_size | Troubleshooting Upload Limits</a>
- → <a href="/ja/blog/php-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">How to Implement File Upload in PHP | Complete Guide to Validation, Storage, and Security</a>
- → <a href="/ja/blog/laravel-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">Laravel File Upload Implementation Guide | Validation, Storage, and S3 Support</a>