# asset_sync pushes Rails precompiled assets to S3, and what its URL rules cost you

> Asset Sync is a Ruby gem that hooks into the Rails asset pipeline so that bundle exec rake assets:precompile uploads digested assets to a cloud bucket. It works with AWS, Google Storage, Azure Blob and Backblaze B2, and its asset host rules impose a real constraint on bucket naming.

**AssetSync/asset_sync** — Synchronises Assets between Rails and S3

- Repository: https://github.com/AssetSync/asset_sync
- Stars: 1,892 · Forks: 333
- Language: Ruby
- License: not declared
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/assetsync-asset-sync

## The gem's whole job is a post-precompile upload

Asset Sync attaches itself to the Rails asset pipeline introduced in Rails 3.1. You run the precompile task as you always would, and when it finishes the digested files are copied to your storage bucket instead of being served off the application's own web server.

```bash
bundle exec rake assets:precompile
```

Two behaviours from the description matter for planning. It can delete files it considers unused in the bucket, and it uploads only the files it needs to rather than re-pushing everything. That is the reason to prefer this over a shell script that mirrors the public directory: the gem knows what the manifest says is current.

It was built for Heroku and the README says so directly. It also states it can work on any platform. The practical test is not the platform but the timing of the precompile step: if precompile runs in a build container whose output never reaches production, this gem has nothing to upload from.

## fog-core means you pick the provider gem yourself

Since version 2.x asset_sync depends on fog-core rather than fog. The stated reason is that fog pulls in many storage provider gems that are unused. The consequence is on you: asset_sync has no idea which provider you intend to use, so you bundle the right one yourself in the Gemfile.

```ruby
gem "asset_sync"
gem "fog-aws"
```

That is the entire S3 install. For Azure Blob storage the README points at gitlab-fog-azure-rm and comments that fog-azure-rm appears unmaintained, which is a maintenance flag worth reading before you choose it. For Backblaze B2 the pair is asset_sync with fog-backblaze.

This indirection is a genuine trade-off rather than a free win. A single dependency on fog would have made the first install simpler and every unused provider would have come along anyway. Splitting it keeps the bundle small and lets the same gem serve AWS, Google Storage Cloud, Azure Blob and Backblaze B2, at the price of one more line in the Gemfile and one more thing that can go stale.

## asset_host decides the URL shape, and the bucket name decides whether it works

The Rails side of the configuration sets config.action_controller.asset_host in config/environments/production.rb, and the form you pick has consequences. The virtual-host style for S3 is:

```ruby
  #config/environments/production.rb
  config.action_controller.asset_host = "//#{ENV['FOG_DIRECTORY']}.s3.amazonaws.com"
```

Google Storage Cloud swaps the domain for storage.googleapis.com, Azure Blob uses blob.core.windows.net with AZURE_STORAGE_ACCOUNT_NAME, and Backblaze B2 uses f000.backblazeb2.com/file/.

Leaving the protocol off is deliberate. Without a scheme the browser picks the transport, so an application reachable over both HTTP and HTTPS serves assets the same way.

The trap is the bucket name. The README warns that the S3 bucket name must not contain any periods, because a name like mydomain.com.s3.amazonaws.com fails under HTTPS: Amazon's certificate does not treat that as a subdomain of s3.amazonaws.com but as a multi-level subdomain. The workaround is the path style URL:

```ruby
  config.action_controller.asset_host = "//s3.amazonaws.com/#{ENV['FOG_DIRECTORY']}"
```

The same style exists for Google Storage Cloud. A separate caveat applies to buckets outside us-east-1: those must use the virtual-host form, since the path style against a non-default region makes Amazon return a 301 permanently moved. In other words one bucket name has to satisfy the certificate rule and one region has to satisfy the routing rule.

## Three configuration sources, and which one keeps keys out of git

Asset Sync reads its settings from a built-in initializer driven by environment variables, from a custom Rails initializer, or from a YAML file. The README describes the YAML route as a common strategy for Capistrano deployments. Its recommendation is environment variables, and the reason given is not elegance: hard coding works fine, but environment variables keep your access keys out of version control.

On Heroku, the variables are added with config:add:

```bash
heroku config:add AWS_ACCESS_KEY_ID=xxxx
heroku config:add AWS_SECRET_ACCESS_KEY=xxxx
heroku config:add FOG_DIRECTORY=xxxx
heroku config:add FOG_PROVIDER=AWS
# and optionally:
heroku config:add FOG_REGION=eu-west-1
heroku config:add ASSET_SYNC_COMPRESSION=gz
heroku config:add ASSET_SYNC_MANIFEST=true
heroku config:add ASSET_SYNC_EXISTING_REMOTE_FILES=keep
```

Four of those optional values are decisions rather than booleans. ASSET_SYNC_COMPRESSION=gz compresses uploads. ASSET_SYNC_MANIFEST=true uploads a manifest with the asset list. FOG_REGION sets the bucket region. ASSET_SYNC_EXISTING_REMOTE_FILES=keep changes what happens to files already in the bucket, which pairs with the delete-unused-files behaviour in the gem description and is the setting to read twice before enabling anything destructive.

On a traditional Unix host the same variables come from export statements. Rackspace uses RACKSPACE_USERNAME and RACKSPACE_API_KEY with FOG_PROVIDER=Rackspace, and Google Storage Cloud configuration is also supported, though the README text for that section is cut off before the variable names appear.

## Settings Rails needs before precompile will do anything

Besides the asset host, the README requires that config.assets.digest and config.assets.enabled are both true, in production.rb or application.rb. Digested filenames are what let the gem upload only changed assets, so turning digest off removes most of the reason to use it.

There is a subtler one. If your application depends on configuration set up in initializers, you must also set config.assets.initialize_on_precompile to true, because otherwise those initializers have not run when the precompile task executes and whatever they configure is missing from the upload.

Assets can also be placed in a subfolder rather than the bucket root:

```ruby
  # store assets in a 'folder' instead of bucket root
  config.assets.prefix = "/production/assets"
```

The prefix also has to match what FOG_DIRECTORY points at, or the upload path and the asset_host path disagree and every asset 404s in production while precompile reports success. Nothing in the README checks this for you.

## Speed advice that is pinned to Rails 3.2 and a deprecated path

The one performance note in the README concerns assets:precompile wall time on Rails 3.2.x, where the main cost is compiling non-digest assets. The suggested remedy is turbo-sprockets-rails3, which compiles only digest assets and, in the README's words, cuts compile time in half.

The note carries its own expiry. It says turbo-sprockets-rails3 will be deprecated in Rails 4 because sprockets-rails has been extracted out of Rails and only compiles digest assets by default. So on any Rails version past that point the advice is obsolete: the problem it solves no longer exists, and the workaround would be adding a dependency for nothing.

Read it as a marker of the README's vintage rather than a current recommendation. The surrounding text still refers to Rails 3.1 and 3.2 as current, which is also consistent with the repository layout: a Rakefile, an Appraisals file and a gemfiles/ directory for testing against multiple Rails versions, plus an UPGRADING.md for people coming from 1.x. The project has no GitHub releases, so the CHANGELOG.md in the repository is where version history lives. The repository is not archived and the last push was on 2026-09-30. The listed licence for the repository is unknown, so check the asset_sync.gemspec and LICENSE situation in the source before relying on it commercially.

## Conclusion

asset_sync suits Rails applications on Heroku or any platform where precompile runs as part of deploy and the digested output should end up in object storage, and it suits teams that want one gem across S3, Google Storage, Azure Blob and Backblaze B2 by swapping the fog provider. It does not suit a build pipeline that already produces a container image with a CDN in front of it, or a project that needs encrypted or signed asset delivery, since the README documents only public asset_host values. Before wiring it up, confirm your bucket name contains no periods if you serve over HTTPS, and confirm that the fog provider gem you choose is one you are willing to bundle, because asset_sync declares no idea which provider you use.

## FAQ

### How do I install asset_sync with Rails?

Add gem "asset_sync" to your Gemfile along with the fog provider gem for the storage you want, for example gem "fog-aws" for S3. Since 2.x asset_sync depends on fog-core instead of fog, so you bundle the provider yourself.

### Which environment variables does asset_sync need for AWS?

AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, FOG_DIRECTORY and FOG_PROVIDER=AWS, with FOG_REGION, ASSET_SYNC_COMPRESSION, ASSET_SYNC_MANIFEST and ASSET_SYNC_EXISTING_REMOTE_FILES as optional additions. They can be set with heroku config:add or with export on a Unix host.

### Why do my S3 assets fail to load over HTTPS?

The README states the bucket name must not contain periods, because a name such as mydomain.com.s3.amazonaws.com is read as a multi-level subdomain and fails certificate validation. The path style asset_host "//s3.amazonaws.com/#{ENV['FOG_DIRECTORY']}" avoids it, but buckets outside us-east-1 must use the virtual host style instead.

### Can asset_sync store assets outside the bucket root?

Yes. Set config.assets.prefix, for example config.assets.prefix = "/production/assets", in production.rb so the files land in a subfolder of the bucket.

### Does asset_sync support storage providers other than S3?

The README documents asset_host examples for Google Storage Cloud, Azure Blob storage and Backblaze B2, with gem pairs including gitlab-fog-azure-rm and fog-backblaze. Rackspace configuration is described with RACKSPACE_USERNAME and RACKSPACE_API_KEY.

### Is turbo-sprockets-rails3 still needed to speed up assets:precompile?

The README presents it as a fix for Rails 3.2.x compile time and states it will be deprecated in Rails 4 because sprockets-rails already compiles digest assets by default. On a current Rails version that optimisation is not needed.

## Sources

- [AssetSync/asset_sync on GitHub](https://github.com/AssetSync/asset_sync)
- [Issues](https://github.com/AssetSync/asset_sync/issues)
- [README](https://github.com/AssetSync/asset_sync/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/assetsync-asset-sync
