How to Configure Remote Storage for Magento 2.x
Magento 2.x supports remote storage for media files, import/export files, and other files. This feature allows you to store files in a remote storage location, such as an Amazon S3 bucket, instead of storing them on the server itself.
This can be useful for many reasons, such as:
Offloading storage from your server, reducing the load on your server, and improving performance.
Allows you to make use of horizontal scaling, as you can easily add more servers without having to worry about syncing files between them.
Allows for effortless storage capacity scaling, as you can easily increase the storage capacity of your remote storage location.
Serving assets from a CDN, which can improve the performance of your website.
Configuring the application
Configuring Magento 2 to start storing files in your bucket is done using a single command.
AWS S3
bin/magento setup:config:set \
--remote-storage-driver="aws-s3" \
--remote-storage-bucket="my_bucket_name" \
--remote-storage-region="my-aws-region" \
--remote-storage-key="abcd1234" \
--remote-storage-secret="abcd1234"
Other S3 compatible providers
If you’re using a different provider than AWS S3, you need to specify the --remote-storage-endpoint
option.
bin/magento setup:config:set \
--remote-storage-driver="aws-s3" \
--remote-storage-bucket="my_bucket_name" \
--remote-storage-region="provider-region" \
--remote-storage-key="abcd1234" \
--remote-storage-secret="abcd1234" \
--remote-storage-endpoint="https://my-s3-compatible.endpoint.com"
Syncing the files
Instead of running (which is Magento’s official way to do this):
bin/magento remote-storage:sync
One can run the following instead to really speed up the process:
aws s3 sync pub/media/ s3://my_bucket_name/media/
aws s3 sync var/import_export s3://my_bucket_name/import_export
This is much faster than Magento’s built-in sync, because aws s3 sync
uploads files concurrently.
The storage flag file in the bucket
Magento’s S3 implementation creates a test file called storage.flag
, which is basically created to test if the connection works. So this is not a magic file to mark anything (source).
Serving assets from your S3 bucket
To start serving media assets from your S3 bucket, you need to make some adjustments to your nginx configuration. Create the following file at /data/web/nginx/example.com/server.assets.conf
for each relevant vhost:
set $backend "haproxy";
location @object_storage_fallback {
# Proxy to object storage
set $bucket "my_bucket_name";
proxy_cache_key "$bucket$uri";
proxy_cache_valid 200 302 7d;
proxy_cache_methods GET HEAD;
proxy_cache_background_update on;
proxy_cache_use_stale updating;
proxy_cache asset_cache;
resolver 8.8.8.8;
proxy_pass https://$bucket.s3.amazonaws.com$uri;
proxy_pass_request_body off;
proxy_pass_request_headers off;
proxy_intercept_errors on;
proxy_hide_header "x-amz-id-2";
proxy_hide_header "x-amz-request-id";
proxy_hide_header "x-amz-storage-class";
proxy_hide_header "x-amz-server-side-encryption";
proxy_hide_header "Set-Cookie";
proxy_ignore_headers "Set-Cookie";
add_header Cache-Control "public";
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Cache-Status $upstream_cache_status;
expires +1y;
# If object storage fails, fallback to PHP handler
error_page 404 = @asset_fallback;
error_page 403 = @asset_fallback;
}
location @php_asset_fallback {
# Handle with phpfpm
rewrite ^/media /get.php?$args last;
rewrite ^/static/(version\d*/)?(.*)$ /static.php?resource=$2 last;
echo_exec @phpfpm;
}
location @haproxy {
# Handle with haproxy
include /etc/nginx/proxy_to_haproxy.conf;
proxy_pass http://127.0.0.1:8080;
}
location @asset_fallback {
try_files "" $asset_fallback_handler;
}
location ~ ^/static/ {
expires max;
# Remove signature of the static files that is used to overcome the browser cache
location ~ ^/static/version\d*/ {
rewrite ^/static/version\d*/(.*)$ /static/$1 last;
}
location ~* \.(ico|jpg|jpeg|png|gif|svg|svgz|webp|avif|avifs|js|css|eot|ttf|otf|woff|woff2|html|json|webmanifest)$ {
add_header Cache-Control "public";
add_header X-Frame-Options "SAMEORIGIN";
expires +1y;
try_files $uri $uri/ @asset_fallback;
}
location ~* \.(zip|gz|gzip|bz2|csv|xml)$ {
add_header Cache-Control "no-store";
add_header X-Frame-Options "SAMEORIGIN";
expires off;
try_files $uri $uri/ @asset_fallback;
}
try_files $uri $uri/ @asset_fallback;
add_header X-Frame-Options "SAMEORIGIN";
}
location /media/ {
try_files $uri $uri/ @asset_fallback;
location ~ ^/media/theme_customization/.*\.xml {
deny all;
}
location ~* \.(ico|jpg|jpeg|png|gif|svg|svgz|webp|avif|avifs|js|css|swf|eot|ttf|otf|woff|woff2)$ {
add_header Cache-Control "public";
add_header X-Frame-Options "SAMEORIGIN";
expires +1y;
try_files $uri $uri/ @object_storage_fallback;
}
location ~* \.(zip|gz|gzip|bz2|csv|xml)$ {
add_header Cache-Control "no-store";
add_header X-Frame-Options "SAMEORIGIN";
expires off;
try_files $uri $uri/ @object_storage_fallback;
}
add_header X-Frame-Options "SAMEORIGIN";
}
Make sure to change the string my_bucket_name
to the name of your bucket and keep in mind that your bucket URL might be different depending on your AWS region. For example, you might need to change it from https://$bucket.s3.amazonaws.com$uri
to https://s3.amazonaws.com/$bucket$uri
instead.
Furthermore, ensure that your S3 bucket policies are configured correctly, so that only /media
is publicly readable. For example:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPublicReadOnlyForMedia",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<your-bucket-name>/media/*"
}
]
}