Configuring Centralized Media File Storage for 1C-Bitrix
In a clustered Bitrix configuration or when multiple environments (prod, staging, dev) are present, media files from /upload/ live locally on each server. An uploaded product image exists on one node but not on others. File replication via NFS works but creates a single point of failure. Centralized storage based on an S3-compatible object store is the standard solution for this class of problems.
Object Storage Options
- Yandex Object Storage — S3-compatible, data stored in Russia, straightforward integration
- AWS S3 — if data can be stored outside the country
- MinIO — self-hosted S3, can be deployed on your own servers
- Selectel Object Storage — Russian hosting with S3 API
All options work with the same API (S3 compatible); Bitrix integration is identical for all.
Bitrix Cloud Storage Module
Bitrix has a built-in bitrix.cloud module for cloud file storage. Configuration: Settings → Cloud Storage.
Supported providers out of the box: Amazon S3, Azure Blob Storage. For Yandex Object Storage — via a custom endpoint, since it is S3-compatible:
// Configuration via .settings.php for S3-compatible storage
// Using direct integration via AWS SDK
Limitation of the bitrix.cloud module: not all file types are migrated correctly (file editor in admin panel, resize cache). Testing on staging is recommended.
Direct Integration via AWS SDK
composer require aws/aws-sdk-php
// /local/lib/Storage/S3Storage.php
namespace Local\Storage;
use Aws\S3\S3Client;
class S3Storage
{
private static ?S3Client $client = null;
public static function getClient(): S3Client
{
if (!self::$client) {
$config = \Bitrix\Main\Config\Configuration::getValue('s3_storage');
self::$client = new S3Client([
'version' => 'latest',
'region' => $config['region'],
'endpoint' => $config['endpoint'], // for Yandex: storage.yandexcloud.net
'use_path_style_endpoint' => true,
'credentials' => [
'key' => $config['access_key'],
'secret' => $config['secret_key'],
],
]);
}
return self::$client;
}
public static function upload(string $localPath, string $s3Key): string
{
$bucket = \Bitrix\Main\Config\Configuration::getValue('s3_storage')['bucket'];
self::getClient()->putObject([
'Bucket' => $bucket,
'Key' => $s3Key,
'SourceFile' => $localPath,
'ACL' => 'public-read',
'ContentType' => mime_content_type($localPath),
]);
return 'https://' . $bucket . '.storage.yandexcloud.net/' . $s3Key;
}
}
Configuration in /bitrix/.settings.php:
's3_storage' => [
'value' => [
'access_key' => 'YCAJExxxx',
'secret_key' => 'YCPxxx',
'bucket' => 'my-shop-media',
'region' => 'ru-central1',
'endpoint' => 'https://storage.yandexcloud.net',
],
],
Intercepting File Uploads in Bitrix
Bitrix saves files via CFile::SaveFile(). To route files to S3 instead of the local disk, intercept via a hook:
// Alternative: override behavior via a post-save event handler
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'main',
'OnAfterFileSave',
function (\Bitrix\Main\Event $event) {
$file = $event->getParameter('FILE');
$localPath = $_SERVER['DOCUMENT_ROOT'] . $file['SRC'];
if (file_exists($localPath)) {
$s3Key = ltrim($file['SRC'], '/');
\Local\Storage\S3Storage::upload($localPath, $s3Key);
// Optional: delete the local copy after uploading to S3
// unlink($localPath);
}
}
);
For serving files — configure nginx to proxy requests to /upload/ through the S3 CDN:
location /upload/ {
proxy_pass https://my-shop-media.storage.yandexcloud.net/upload/;
proxy_cache_valid 200 7d;
add_header Cache-Control "public, max-age=604800";
}
Migrating Existing Files
Moving the current /upload/ to S3 is a separate operation:
# Sync local upload/ to S3 via AWS CLI
aws s3 sync /var/www/bitrix/upload/ s3://my-shop-media/upload/ \
--endpoint-url https://storage.yandexcloud.net \
--acl public-read \
--no-progress
# Verify file count
aws s3 ls s3://my-shop-media/upload/ --recursive --endpoint-url https://storage.yandexcloud.net | wc -l
Migration is done with rollback capability: local files are not deleted until correct operation is confirmed.
Setup Timeline
S3 storage setup, Bitrix integration via SDK, nginx proxy configuration, existing file migration — 2–4 business days depending on the size of the existing upload/ directory.







