Active Storage on Heroku
Last updated 08 May 2018
Rails 5.2 introduced Active Storage as a way of managing attaching and saving files to Active Record models. This guide will cover how to use Active Storage on Heroku.
Heroku has an “ephemeral” hard drive, this means that you can write files to disk, but those files will not persist after the application is restarted. By default Active Storage uses a
:local storage option, which uses the local file system to store any uploaded files. While file uploads that are stored with the
:local option will appear to work at first, the attachments will exhibit seemingly strange behavior and eventually disappear. The files will go away when the app is deployed, or when it is automatically restarted (once every 24 hours). If the app has multiple dynos, not all files will be present on every dyno. This means that the dyno that serves a web request, might be different than a dyno that contains a specific uploaded file. For example if you have two dynos, and upload a file, it will only be present on one dyno. When you refresh the webpage, there will be a 50% chance that the web request will be routed to the dyno with the file, and a 50% chance it will appear to be broken. In addition, any files stored on disk will not be visible from one-off dynos such as a
heroku run bash instance or a scheduler task because these commands use new dynos.
Instead of storing uploaded files to disk, the best practice is to leverage a cloud file storage service such as Amazon’s S3.
amazon: service: S3 access_key_id: <%= ENV['BUCKETEER_AWS_ACCESS_KEY_ID'] %> secret_access_key: <%= ENV['BUCKETEER_AWS_SECRET_ACCESS_KEY'] %> region: <%= ENV['BUCKETEER_AWS_REGION'] %> bucket: <%= ENV['BUCKETEER_BUCKET_NAME'] %>
Once you’ve done this, you will need to tell your application to use this storage backend in production. In your
config/environments/production.rb make sure that you have set your active storage service to amazon:
config.active_storage.service = :amazon
Finally you need to also include the AWS gem. Add this to your
gem "aws-sdk-s3", require: false
Don’t forget to run this command locally:
$ bundle install
Then commit your files to git before trying to deploy to Heroku.
One of the marquee features of Active Storage is the ability to use “previews” of non-image attachments. Specifically you can preview PDFs and Videos. To use this feature your application needs access to system resources that know how to work with these files. By default Rails ships with support with
poppler for PDF previews, and
ffmpeg for Video previews. These system dependencies are not available by default on Heroku.
If you want the ability to preview these types of files with Active Support you need to run:
$ heroku buildpacks:add -i 1 https://github.com/heroku/heroku-buildpack-activestorage-preview
Once you’ve done this, you need to deploy again to get the binaries. You can verify that the dependencies are installed by running
which ffmpeg on the command line. If there is no output then the operation was not performed correctly. If you see a result then the binaries are ready to be used:
$ heroku run bash ~$ which ffmpeg /app/.heroku/activestorage-preview/usr/bin/ffmpeg
If you try to preview a PDF without
poppler, the page will error and you’ll see this in your logs:
If you try to preview a video without
ffmpeg, the preview will appear to be broken and you’ll see an error like this in your logs:
Errno::ENOENT (No such file or directory - ffmpeg):