Paperclip, Heroku and Amazon S3 credentials 4

Posted by Slobodan Kovačević on January 24, 2011

Setting up Paperclip to use Amazon’s S3 is as simple as setting :storage => :s3 and providing right credentials to Paperclip by setting :s3_credentials option. Best way to provide S3 credentials is to use an YML file (usually config/s3.yml) which allows you to set different credentials for each environment. For example:

# config/s3.yml
development:
  access_key_id: XYZXYZXYZ
  secret_access_key: XYZXYZXYZ
  bucket: mygreatapp-development
production:
  access_key_id: XYZXYZXYZ
  secret_access_key: XYZXYZXYZ
  bucket: mygreatapp-production

Of course you want to treat s3.yml same as database.yml – i.e. you don’t want to track it with git and you want for each person/server to have it’s own.

However, consider this: you are working on Open Source app in a public git repository and you are deploying it on Heroku. Heroku doesn’t allow you to create files (unless they are in git repository) and you can’t commit s3.yml with your credentials to public repository.

One solution is to define different :s3_credentials hash in one of the environment files or to load different YML file for each environment and generate hash from it. Downside is that you need to have a separate YML file for each environment and/or you need to convert YML to hash. Other solution could be to have separate local branch from which you will push to Heroku. Problem with this is that you have to have a local branch for deploying. This means if there are multiple developers who deploy to production each should have separate local branch.

Much simpler way to deploy Paperclip with different S3 credentials for each environment (with one of the environment being deployed on Heroku; and repository being public) is to create s3.yml file as usual (and don’t commit it to git), but define values only for local environment.

For production deployment on Heroku you can write initializer which will set :s3_credentials from ENV variables.

# initializers/s3.rb
if Rails.env == "production"
  # set credentials from ENV hash
  S3_CREDENTIALS = { :access_key_id => ENV['S3_KEY'], :secret_access_key => ENV['S3_SECRET'], :bucket => "sharedearth-production"}
else
  # get credentials from YML file
  S3_CREDENTIALS = Rails.root.join("config/s3.yml")
end

# in your model
has_attached_file :photo, :storage => :s3, :s3_credentials => S3_CREDENTIALS

and you can easily set persistant ENV vars on Heroku with:

$ heroku config:add S3_KEY=XYZXYZ S3_SECRET=XYZXYZ

(according to Heroku docs)

Share
Trackbacks

Use this link to trackback from your own site.

Comments

Leave a response

  1. Swards Mon, 11 Apr 2011 19:05:39 UTC

    Exactly what I needed. I’m in the process of open-sourcing an app I’m building. I was considering doing this in the database, but wasn’t sure how I would do that. This makes more sense. Thanks for posting!

  2. Dorian Sat, 28 May 2011 12:52:45 UTC

    Thansks Slobodan, I was looking for a simple way to do this and I like your solution! Keep up the good work!

  3. Tom Thu, 17 Nov 2011 03:05:42 UTC

    Thanks slobodan. I am new to ruby/rails and I am deploying my first app using paperclip/heroku. I like this solution, but one question: do I need two different buckets on S3, one for production, one for development?

    Thanks!
    Tom

  4. Slobodan Kovačević Thu, 17 Nov 2011 09:33:03 UTC

    @tom in my case I wanted to use S3 in development too, so I created two buckets to keep production and development files separate. You can also play around and make development use local file storage and use S3 only on production.

Comments