How to Integrate Amazon S3 with Drupal: A Step-by-Step Guide
Omar Alahmed
February 15, 2023
Updated on:
August 17, 2026
Integrating Amazon S3 with Drupal is handled by the S3 File System (s3fs) module. Install it via Composer, create an S3 bucket in AWS, create a scoped IAM user for it, then supply the access key, secret key, bucket name, and region through settings.php. You can route all public files to S3 or enable it per field. Field-level is usually the better choice.
Drupal sites accumulate media faster than anything else on the platform, and local disk is the wrong place for it at scale. Moving files to Amazon S3 gives you effectively unlimited storage, takes media off your application servers, and makes horizontal scaling possible without a shared filesystem. This guide walks through the integration end to end.
Why Use Amazon S3 with Drupal?
For storage scale and server independence, not for raw speed. That distinction matters, and it is worth being precise about before you scope the work.
Amazon Simple Storage Service (S3) is a cloud object storage service from Amazon Web Services. Pairing it with Drupal moves your file assets off the application server and into object storage that scales independently of your hosting.
Three benefits are real and worth the setup:
Scalability. S3 grows with your media library without a disk resize or a shared filesystem between web nodes. On a multi-server Drupal deployment this is often the reason to adopt it, not a side benefit.
Server independence. Files no longer live on the machines serving your site, so you can scale web nodes horizontally, rebuild them, or replace them without migrating a files directory.
Durability and access control. S3 provides versioning, server-side encryption, and granular access policies that a local files directory does not.
On performance, be precise: S3 by itself is object storage, not a content delivery network. Moving files to S3 removes load from your web servers, but a request from a user in Europe to a bucket in us-east-1 can be slower than serving that file locally. If latency is your goal, put CloudFront (or another CDN) in front of the bucket and serve media through the CDN domain. S3 solves the storage problem; the CDN solves the distance problem.
Through the S3 File System (s3fs) module, in six steps. Steps 1 to 3 happen in AWS, steps 4 to 6 in Drupal. The module handles storing and serving Drupal site assets, including images, videos, audio, and other file types, from an S3 bucket.
Step 1: Create Your AWS Account
Visit the AWS homepage and follow the sign-up instructions. After creating your account, you will have access to the AWS Management Console, where you can create your S3 bucket.
Step 2: Create the S3 Bucket
Log in to your AWS Management Console.
Go to the S3 service page.
Click Create bucket.
Enter a unique name for your bucket. Bucket names are globally unique across all AWS accounts, so include your project name.
Select the region where you want your bucket located. Choose the region closest to your hosting, not to your users, since a CDN will handle user proximity.
Choose the default settings, or customize them to meet your specific needs.
Click Create bucket.
Step 3: Create a Scoped IAM User
Do not use root account credentials for this. Create a dedicated IAM user whose permissions are limited to the single bucket Drupal will use, and generate the access key against that user. If the key is ever exposed, the blast radius is one bucket rather than your entire AWS account.
A minimal policy grants read, write, delete, and list on that bucket only:
Adjust to your environment. Some setups will need additional actions, and if your hosting runs on EC2 or ECS you can skip static keys entirely by attaching an IAM role to the instance.
Configure it at /admin/config/media/s3fs, or in settings.php:
// S3FS settings
$settings['s3fs.access_key'] = getenv('S3FS_ACCESS_KEY');
$settings['s3fs.secret_key'] = getenv('S3FS_SECRET_KEY');
$config['s3fs.settings']['bucket'] = 'S3 BUCKET NAME';
$config['s3fs.settings']['region'] = 'S3 BUCKET REGION'; // For example: us-east-1
$settings['s3fs.use_s3_for_public'] = TRUE; // Only if you want ALL public files on S3
Verify the module is working by navigating to Configuration > Media > File system and checking that the S3 file system is listed as a download method.
Never hardcode the keys. Read them from environment variables, as above, or from a settings.local.php that is excluded from version control. An AWS access key committed to a repository is a credential leak, and public repositories are scanned for exactly this pattern within minutes. If a key has already been committed, rotate it in IAM rather than deleting the commit.
Each S3 bucket has its own access key, secret key, name, and region. On a team, these are normally provisioned by whoever owns the AWS account, such as your DevOps engineers, and delivered through a secrets manager rather than over chat or email.
Step 5: Configure Upload Fields
To route a specific field to S3, navigate to the content type that has the upload field, click the Manage fields tab, and click Edit next to the field. Change the Upload destination option to the S3 file system.
Once the module is enabled, two new download methods appear in the Drupal file system settings at /admin/config/media/file-system, where you can set the site's default download method:
In the screenshot below, a specific media field is configured to be managed by S3 File System rather than the site default:
To verify, confirm the images render correctly and then inspect the image source in the browser. It should show an external path beginning with https://BUCKET-NAME.s3.amazonaws.com/, or your CloudFront domain if you have configured one.
Step 6: Configure S3 for Private Files
While s3fs is primarily used for a site's public files, private files can be stored on S3 too. Certain media that should only be accessible to authenticated users or site administrators may need to live in the bucket while remaining access-controlled.
Enable the private files options on the module settings page, or add these two lines to settings.php:
$settings['s3fs.use_s3_for_private'] = TRUE; // Use S3 for private files
$settings['s3fs.upload_as_private'] = TRUE; // Upload files as private
With these settings, files uploaded to the private file system are served through the site URI rather than directly from the bucket, so Drupal's access checks still apply. Confirm this after configuration by copying a private file's direct S3 URL and requesting it while logged out. It should be refused.
If you have a separate S3 bucket for testing, override the module settings in your site's settings.local.php. Keeping non-production environments on their own bucket prevents a staging test from overwriting production media.
Should You Route All Files to S3, or Just Specific Fields?
Field level, in most cases. Setting use_s3_for_public to TRUE sends everything to S3, including your aggregated CSS and JavaScript, which are the assets most sensitive to latency and the ones your webserver already handles well.
All public files on S3
Field-level configuration
What moves
Everything, including aggregated CSS and JS
Only the fields you designate
Theme asset latency
Adds a round trip to S3 for every page render
Served locally at webserver speed
Where it's set
One line in settings.php
Per field, under Manage fields
Setup effort
Lowest
Higher, and grows with the content model
Best for
Multi-server deployments with no shared filesystem, where local disk is not an option
Most sites, especially media-heavy ones on single-server or managed hosting
The recommendation: keep the default download method as Public local files served by the webserver, and configure S3 at the field level for the media that actually needs it. Your CSS and JS stay local with lower latency, and your image and video libraries move off local disk. Go global only when a shared filesystem is genuinely unavailable across your web nodes.
What Goes Wrong, and How Do You Fix It?
Three problems account for most s3fs support threads.
Images Upload Successfully but Render as Broken
Usually a permissions mismatch between the IAM policy and how the module writes objects, or a bucket-level public access block preventing reads. Check the IAM policy includes s3:PutObjectAcl, and confirm the bucket's public access settings match the way you intend files to be served. If you are serving through CloudFront, verify the distribution's origin access configuration rather than the bucket policy alone.
Files Exist in the Bucket but Drupal Cannot See Them
s3fs keeps a local metadata cache of bucket contents so it does not have to query S3 on every file operation. Files added to the bucket outside Drupal, or a migration that wrote directly to S3, will not appear until that cache is refreshed. The module ships a Drush command for this; check drush list | grep s3fs for the exact syntax on your version. Run it after any bulk operation that touched the bucket directly.
Everything Works Locally and Fails on Production
Almost always an environment variable that was set in one place and not the other. Confirm the credentials are actually present in the production environment, and that the region matches the bucket. A region mismatch produces an authentication-style error that looks like a credentials problem and is not.
What Should You Do Next?
Decide on field-level or global before you install anything, because reversing that choice later means re-pointing existing file references. For most sites the sequence is: create the bucket, scope an IAM user to it, install s3fs, configure the media fields that carry the bulk of your storage, then add CloudFront if latency rather than storage was the original problem.
Vardot builds and maintains enterprise Drupal platforms as a Drupal Diamond Certified Partner, with 200+ platforms launched and a 4.9/5 Clutch rating across verified reviews. If you are planning an S3 migration for an existing media library, the file reference migration is usually the part that needs the most planning, and it is worth scoping before the module goes in.
Planning an S3 migration for an existing Drupal media library?
Omar Alahmed is the Director of Engineering at Vardot, with nearly twenty years of Drupal experience ranging from version 5 to the current core. He specializes in enterprise platforms for higher education, government, NGOs, and mission-driven organizations worldwide, with a core focus on blending optimized performance, robust security, and SEO with strict digital accessibility.
Yes. The S3 File System module (s3fs) supports Drupal 10 and Drupal 11 on its current 8.x-3.x branch. It installs through Composer, which automatically pulls in AWS SDK for PHP v3 as a dependency. The module requires allow_url_fopen = On in your PHP configuration and the cURL library so the SDK can communicate with S3.
In most cases, keep aggregated CSS and JS served locally for lower latency, and use S3 for media assets like images, video, and downloads. Since Drupal core 10.1, aggregated CSS/JS use a separate assets:// stream wrapper, so storing them on S3 requires the companion S3 File System for assets module rather than the core s3fs configuration.
Yes. The S3 File System module manages private files when you enable s3fs.use_s3_for_private and s3fs.upload_as_private in settings.php, and it can issue presigned URLs for time-limited access. When public and private files share one bucket, configure the schemes carefully a past s3fs security advisory involved access bypass between filesystem schemes stored in the same bucket.
Yes. The S3 File System module works with any S3-compatible object storage, including MinIO, DigitalOcean Spaces, and Backblaze B2, not just Amazon S3. You point the module at the provider's endpoint and supply that provider's credentials. This makes s3fs a practical option for teams with data-residency requirements or an existing non-AWS storage contract.
Amazon S3 integration matters most for Drupal sites load-balanced across multiple servers, where Drupal's default local file system can't keep files synchronized between nodes. Storing media in a central S3 bucket solves that synchronization problem, offloads storage from web servers, and, when paired with a CDN such as CloudFront, improves media delivery speed for global audiences.