Using Local Volumes for Development

If your assets are stored in remote volumes, such as Amazon S3, Google Cloud Storage, or DigitalOcean Spaces, then you may wish to use a local volume for your development environment.

This is possible using Yii’s dependency injection container, which is a powerful tool that can give you fine-grained control over how each system component is initialized.

Make sure you’re running Craft 3.5 or higher before following these instructions.

Add the dependency injection code #

First you will need to create a module. You can use the one that came with your Craft installation by default if you used the craftcms/craft starter project, or you can generate a new one from

Add this code to the end of your module’s init() method:

// If this is the dev environment, use Local volumes instead of S3
if (\Craft::$app->env === 'dev') {
    \Craft::$container->set(\craft\awss3\Volume::class, function($container, $params, $config) {
        if (empty($config['id'])) {
            return new \craft\awss3\Volume($config);

        return new \craft\volumes\Local([
            'id' => $config['id'],
            'uid' => $config['uid'],
            'name' => $config['name'],
            'handle' => $config['handle'],
            'hasUrls' => $config['hasUrls'],
            'url' => "@web/local-volumes/{$config['handle']}",
            'path' => "@webroot/local-volumes/{$config['handle']}",
            'sortOrder' => $config['sortOrder'],
            'dateCreated' => $config['dateCreated'],
            'dateUpdated' => $config['dateUpdated'],
            'fieldLayoutId' => $config['fieldLayoutId'],

(If you are using something besides Amazon S3, replace craft\awss3\Volume with the actual volume class.)

Then open config/app.php and make sure your module’s ID is listed in the bootstrap array (and that the line isn’t commented out). Bootstrapping your module ensures that it will get loaded at the beginning of every request.

With that code in place, all of your S3 volumes will suddenly become Local volumes, and their Base URL and File System Path settings will point to a local-volumes/<handle>/ folder within your site’s webroot.

Keep in mind that your dependency injection code will be in effect at all times for your dev environment, so you will need to comment it out when you need to edit an existing volume’s settings, or rebuild your project config.

Gitignore the local-volumes/ folder #

We don’t want that local-volumes/ folder to be tracked by Git, so add its path to the .gitignore file at the root of your project:


If your site’s webroot is located somewhere besides web/, replace that with the actual webroot path.