Render에서 PHP/Node.js 파일 업로드 설정하는 방법 | Disk·환경 변수·S3 연동
Render는 Heroku의 대체제로 인기 있는 클라우드 플랫폼입니다. Vercel이나 Netlify와 달리 서버리스가 아닌 상주 프로세스(Web Service) 모델을 사용하므로 파일 업로드 아키텍처가 다릅니다. 본 문서에서는 Render의 영구 스토리지(Disk) 설정, PHP 환경에서 <code>upload_max_filesize</code> 수정 방법, 환경 변수를 사용한 S3 설정, Infrastructure as Code(IaC)의 <code>render.yaml</code>(Blueprint) 설정 예시까지 설명합니다.
Render Disk(영구 스토리지)란?
Render의 Web Service는 기본적으로 에피메랄(재배포 시 파일 시스템 리셋)입니다. 업로드된 파일을 유지하려면 Disk를 추가합니다.
| 스토리지의 종류 | 영속성 | 여러 인스턴스 간 공유 | 비용 |
|---|---|---|---|
| 임시 디스크(기본값) | 재배포시 리셋 | 불가능 | 무료 |
| Render Disk (영구) | 영구적(수동 삭제까지 유지) | 불가능 (1인스턴스만 가능) | $0.25/GB/월 |
| AWS S3 (외부) | 영구적 | 지원됨 | S3 요금 준수 |
Render Disk는 스케일 아웃(다중 인스턴스)을 지원하지 않으므로 프로덕션 파일 업로드에는 S3 연동이 권장됩니다. Disk는 개발, 스테이징 환경 또는 단일 인스턴스 배포에 적합합니다.
Render Dashboard에서 Disk 설정
Disk는 Render Dashboard의 Web Service 설정에서 추가할 수 있습니다.
# render.yaml(Blueprint)での Disk 設定
services:
- type: web
name: my-app
runtime: node
buildCommand: npm install && npm run build
startCommand: npm start
disk:
name: uploads-disk
mountPath: /app/uploads # アプリ内でのマウントパス
sizeGB: 10 # 10GB のディスク
envVars:
- key: UPLOAD_DIR
value: /app/uploads
// Node.js での Disk へのファイル保存
const path = require('path');
const fs = require('fs');
const UPLOAD_DIR = process.env.UPLOAD_DIR || path.join(__dirname, 'uploads');
// アップロードディレクトリが存在しない場合は作成
if (!fs.existsSync(UPLOAD_DIR)) {
fs.mkdirSync(UPLOAD_DIR, { recursive: true });
}
// multer の設定例(Disk ストレージ)
const multer = require('multer');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, UPLOAD_DIR);
},
filename: (req, file, cb) => {
const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
const ext = path.extname(file.originalname);
cb(null, `upload-${uniqueSuffix}${ext}`);
},
});
const upload = multer({
storage,
limits: { fileSize: 50 * 1024 * 1024 }, // 50MB
fileFilter: (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('許可されていないファイル形式です。'));
}
},
});
PHP 환경에서의 upload_max_filesize 설정
Render의 PHP 앱(Docker 기반)의 경우 <code>php.ini</code> 설정을 수정하여 업로드 크기를 조정할 수 있습니다. Render는 임의의 Docker 설정을 지원하므로 다른 호스팅 서비스보다 유연합니다.
# Dockerfile(PHP アプリ)
FROM php:8.2-fpm
# 必要な拡張をインストール
RUN docker-php-ext-install pdo pdo_mysql
# php.ini の設定をオーバーライド
RUN echo "upload_max_filesize = 100M" > /usr/local/etc/php/conf.d/uploads.ini \
&& echo "post_max_size = 110M" >> /usr/local/etc/php/conf.d/uploads.ini \
&& echo "memory_limit = 256M" >> /usr/local/etc/php/conf.d/uploads.ini \
&& echo "max_execution_time = 120" >> /usr/local/etc/php/conf.d/uploads.ini \
&& echo "max_input_time = 120" >> /usr/local/etc/php/conf.d/uploads.ini
WORKDIR /var/www/html
COPY . .
EXPOSE 9000
CMD ["php-fpm"]
# php.ini(直接配置する場合)
; ファイルアップロードの有効化
file_uploads = On
; 1ファイルあたりの上限
upload_max_filesize = 100M
; POST データ全体の上限(upload_max_filesize より大きくする)
post_max_size = 110M
; PHP スクリプトのメモリ上限
memory_limit = 256M
; スクリプトの最大実行時間(秒)
max_execution_time = 120
; ファイル入力待ちの最大時間(秒)
max_input_time = 120
; 一時ファイルのディレクトリ(Render の場合は /tmp を使用)
upload_tmp_dir = /tmp
<?php
// .htaccess が使えない場合は ini_set() で実行時に変更(共有ホスティングでは制限あり)
// Render(Docker)では Dockerfile での設定が確実
// ini_set() による実行時オーバーライド(一部の設定のみ有効)
ini_set('memory_limit', '256M');
// upload_max_filesize と post_max_size は実行時変更不可(php.ini での設定が必要)
// 設定値の確認
echo ini_get('upload_max_filesize'); // "100M"
echo ini_get('post_max_size'); // "110M"
echo ini_get('memory_limit'); // "256M"
환경 변수를 사용한 S3 설정
Render Dashboard 또는 <code>render.yaml</code>에서 환경 변수를 설정하고 앱에서 AWS S3에 액세스합니다. Render의 시크릿 관리 기능을 사용하여 민감한 정보를 관리하고, <code>render.yaml</code>에는 키만 기재합니다.
# render.yaml(Blueprint)— シークレットはキーのみ定義
services:
- type: web
name: my-php-app
runtime: docker
dockerfilePath: ./Dockerfile
envVars:
# 非シークレット(値を直接記載)
- key: AWS_REGION
value: ap-northeast-1
- key: AWS_S3_BUCKET
value: my-app-uploads
# シークレット(Render Dashboard で値を設定)
- key: AWS_ACCESS_KEY_ID
sync: false # sync: false でダッシュボードで手動入力
- key: AWS_SECRET_ACCESS_KEY
sync: false
# または Render の Environment Group を参照
- fromGroup: aws-credentials
disk:
name: tmp-uploads
mountPath: /tmp/uploads
sizeGB: 5
<?php
// PHP での AWS SDK を使った S3 アップロード
require 'vendor/autoload.php';
use Aws\S3\S3Client;
use Aws\Exception\AwsException;
$s3 = new S3Client([
'version' => 'latest',
'region' => getenv('AWS_REGION'),
'credentials' => [
'key' => getenv('AWS_ACCESS_KEY_ID'),
'secret' => getenv('AWS_SECRET_ACCESS_KEY'),
],
]);
function uploadToS3(array $file, string $bucket): array
{
global $s3;
$key = 'uploads/' . uniqid('file_', true) . '_' . basename($file['name']);
try {
$result = $s3->putObject([
'Bucket' => $bucket,
'Key' => $key,
'SourceFile' => $file['tmp_name'],
'ContentType' => mime_content_type($file['tmp_name']),
'ACL' => 'private',
]);
return [
'success' => true,
'key' => $key,
'url' => $result['ObjectURL'],
];
} catch (AwsException $e) {
return ['success' => false, 'error' => $e->getMessage()];
}
}
// アップロード処理
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
$file = $_FILES['file'];
// バリデーション
$maxSize = 50 * 1024 * 1024; // 50MB
if ($file['size'] > $maxSize) {
http_response_code(413);
echo json_encode(['error' => 'ファイルが大きすぎます(上限50MB)。']);
exit;
}
$allowedMimes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
$detectedMime = mime_content_type($file['tmp_name']); // サーバー側でMIMEを検出
if (!in_array($detectedMime, $allowedMimes)) {
http_response_code(415);
echo json_encode(['error' => '許可されていないファイル形式です。']);
exit;
}
$result = uploadToS3($file, getenv('AWS_S3_BUCKET'));
echo json_encode($result);
}
render.yaml(Blueprint)의 완전한 설정 예제
Blueprint를 사용하면 Render의 인프라를 코드로 관리할 수 있습니다. Web Service·데이터베이스·Redis·Disk·환경 변수를 함께 정의할 수 있습니다.
# render.yaml
version: "1"
services:
# Web アプリケーション
- type: web
name: file-upload-app
runtime: node
plan: starter # free / starter / standard / pro
region: singapore # oregon / frankfurt / singapore / ohio
buildCommand: npm ci && npm run build
startCommand: node server.js
healthCheckPath: /health
autoDeploy: true # Git push で自動デプロイ
# 永続ストレージ(スケールアウト不可)
disk:
name: user-uploads
mountPath: /app/data/uploads
sizeGB: 20
# 環境変数
envVars:
- key: NODE_ENV
value: production
- key: PORT
value: 3000
- key: UPLOAD_DIR
value: /app/data/uploads
- key: AWS_REGION
value: ap-northeast-1
- key: AWS_S3_BUCKET
value: my-production-bucket
# シークレット(ダッシュボードで値を設定)
- key: AWS_ACCESS_KEY_ID
sync: false
- key: AWS_SECRET_ACCESS_KEY
sync: false
- key: DATABASE_URL
fromDatabase:
name: app-db
property: connectionString
# PostgreSQL データベース
- type: pserv
name: app-db
runtime: postgres
plan: starter
region: singapore
databaseName: app_production
이 기사에서 사용할 수 있는 테스트 파일 (무료)
- → <a href="/ja/files/images/png/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">PNG 테스트 이미지 (1MB)</a> — Render Disk·S3 업로드 동작 확인용
- → <a href="/ja/files/pdf/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">PDF 테스트 파일(1MB)</a> — PHP의 <code>upload_max_filesize</code> 설정 동작 확인용
- → <a href="/ja/files/zip/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">ZIP 테스트 파일 (1MB)</a> — 경계값 테스트 및 S3 직접 업로드 검증용
관련 기사
- → <a href="/ja/blog/vercel-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">Vercel 파일 업로드 설정과 제한 | Vercel Blob・API 제한・회피 방법</a>
- → <a href="/ja/blog/netlify-upload-config/" class="text-primary-600 dark:text-primary-400 hover:underline">Netlify의 Functions에서 파일을 업로드하는 방법|상한·Large Media·회피책</a>
- → <a href="/ja/blog/laravel-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">Laravel 파일 업로드 구현 가이드|검증·Storage·S3 대응</a>