Words and Code

One writer’s journey from words to code.

Safely Storing Your Assets: Paperclip With AWS + Heroku

#technicaltuesdays, aws, heroku, rails, ruby

We all know how this scene plays out: you’ve created the first iteration of your application and you’re ready to test it out in production. You can’t wait to see what it looks like live, and you want people to be able to start playing with it! Everything seems to go smoothly without any glitches while deploying until you go to your app on Heroku and see…a broken image icon. Tada!


This happened to me recently, and boy was it disheartening. I was all excited because I had used paperclip to get some pretty cool image uploading functionality on my application, but it didn’t seem like it was working. It turns out, however, that an application in development is one thing; the same app in production is another game entirely.

While developing an application, our environments are configured in a certain way. It’s easy to forget that things will change once we deploy what we’ve been working on. Being able to implement something like the paperclip gem in development doesn’t easily carry over to your production environment – unless, of course, you know what tool to use. Luckily, there’s a great gem that was created specifically to make the transition from development to production quick and easy: the aws-sdk gem, Amazon Web Services’ Software Development Kit that was created to use with Rails applications for storing your static assets in your AWS Simple Storage bucket. And once you know how to set it up with your Heroku account, you can get rid of that pesky broken image icon once and for all!

Street Cred For The Web: S3 Credentials

Amazon Web Services’ Simple Storage Service (S3) is a one of the most important platform services that a web developer can use. At its most basic level, it’s a store that allows us to store important assets that we’ll need for our application, including things like media files, pdfs, attachments, and anything that our users may upload.

The really cool thing about AWS is that it allows us to store all those files offsite – that is to say, not on our Heroku application, and instead using a third-party service. Pretty amazing, right?


This is particularly important as our application starts to grow, and we are forced to scale to size. It also makes our application super fast, since our files aren’t being stored within our application itself, which means that we don’t need to use our filesystem to retrieve all those assets.

When it comes to integrating your application with AWS S3, however, the most important step is establishing your credentials. In fact, we can’t even hook up our paperclip configurations – or really, any configurations, for that matter – without our S3 credentials. So, where can we find them?

Well, first we need to know where to look. And in order to know where to look, we have to know how S3 is structured. Thankfully, S3’s architecture is pretty simple to understand: everything is just contained in a bucket. We can name our bucket anything that we want, so long as it’s a unique name across the Amazon system. A common convention is to name your bucket similarly to your application or repository’s name (for example, booklit-website). Once we name our bucket, all of our files sent to S3 will be stored inside and belong to our bucket. An important thing to note is that there are no directories within a bucket; the S3 bucket itself is a top-level container.

Now, how can we access this unique bucket of ours? Using our credentials, of course! In order for us to access the S3 API, we need two things: an AWS Access Key ID and an AWS Secret Access Key. The access key id identifies our S3 user account, while the secret access key is our “password” (at least we should think of it as a password, it’s a bit more complicated than that) for our account.

We can get these two pieces of information from the “Security Credentials” section of the AWS console menu when we log into our AWS account. But these keys aren’t really helpful until we hook up our S3 bucket with our application. I suppose that’s what we should do next!

Keeping Secrets With .env


There’s one golden rule when it comes to our credentials: we should never, ever store them in a file or commit them to source control. We never, ever want our credentials to be potentially exposed to anyone via our Github repository, and yet we definitely need our credentials to exist somewhere within our application, don’t we? Otherwise we won’t be able to tell paperclip where to upload our files and assets. Uh oh…what do?

Enter environment variables to the rescue! There are a few different ways to use environment variables in a Rails application to store our credentials without actually committing them into a file. My favorite way to do this is by creating a .env file in the top-level directory of my application (in our case, booklit-website/.env). Because this file has a . at the beginning, it’s actually a hidden file and won’t be committed. To double check that this is the case, we can open up our .gitignore file, and make sure that our .env file is included as one of the files that Git should “ignore” while committing:

1
2
3
4
5
# See https://help.github.com/articles/ignoring-files for more about ignoring files.

# Ignore Environment Configuration Files
config/database.yml
.env

This means that we can safely put our important AWS credentials inside of our .env file:

1
2
3
4
AWS_ACCESS_KEY_ID=our-id-goes-here
AWS_SECRET_ACCESS_KEY=our-access-key-goes-here
AWS_S3_REGION=us-east-1
AWS_S3_BUCKET=booklit-website-bucket

In addition to the Access Key Id and Secret Access Key that we grabbed from our account information page on the AWS S3 portal, we also put in our S3 Region (the US East Coast), and our S3 Bucket Name (don’t try to hack it because it is very much not a real thing).

Okay, this hidden file with our environment variables is cool and all, but how do we actually retrieve these variables? Well, it’s actually pretty easy. We can treat the variables in our .env file like items in a hash; if we want to access the value of an item in a hash, we can just reference the value via its key, right? The same applies here. If we want to access our AWS_S3_BUCKET name, we can use ENV['AWS_S3_BUCKET'] anywhere in our application in order to access it!

Awesome! We have just one last step before we can finally hook up all of these variables with paperclip and Heroku: we need to create an .env.example file! This is a pretty neat convention that I really liked the first time I saw it, and have since adopted. You’ll remember that our .env file is being ignored by our .gitignore file whenever we commit our repository or push it up to Github. This is great, obviously, since we don’t want our private credentials to ever be public. There is one downside, however: if someone clones our repository, they’ll never know that we even have a .env file, much less what environment variables we have defined inside of it, which means that when they try to run our application or worse, when they try to collaborate with us on it, they’ll hit a huge roadblock, and ruby will break and not know where to look while trying to read something like ENV['AWS_S3_BUCKET'] from within the application.

There’s a quick fix for this: create an “example” .env file called – you guessed it – .env.example. We should make sure that this does not get included in our .gitignore file. Instead, we want it to be committed with our repository. It’s also important to note that we’re still not going to include our credentials in here; instead, all we’ll do is setup our .env.example file as an example of how someone else should create their .env file when they clone or fork our repository:

1
2
3
4
AWS_ACCESS_KEY_ID=xxxxxx
AWS_SECRET_ACCESS_KEY=xxxxxx
AWS_S3_REGION=xxxxxx
AWS_S3_BUCKET=xxxxxx

Now that we’ve finally got all these variables right where we want them, we can finally get to the good stuff!

Configuring Paperclip And Heroku

It may seem like we spent a lot of time setting things up, but it’s all about to come together! We initially started off planning to use the aws-sdk gem. Now it’s time to implement it in the context of the paperclip gem and configure it to work with our Heroku deploy.

First we’ll add it to our Gemfile (gem 'aws-sdk', '~> 2') and then run bundle install. Now our Gemfile should have both aws-sdk and paperclip.

Next, we’ll want to head over to where all the configurations for our production environment are – namely, our production.rb file, which exists inside of our config/environments directory. It’s important to make sure that we’re not setting any Heroku configurations inside of our development.rb file, which only runs inside of our development environment. Since we know we’ll be deploying only the production environment configurations to Heroku, all of our Heroku setup should always live inside of our production environment.

Inside of our production.rb file, we’ll want to add the following configurations:

1
2
3
4
5
6
7
8
config.paperclip_defaults = {
  storage: :s3,
  s3_credentials: {
    bucket: ENV['AWS_S3_BUCKET'],
    access_key_id: ENV['AWS_ACCESS_KEY_ID'],
    secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
  }
}

These lines are how we specify AWS configuration variables for our production environment. If we wanted to test whether we had set all this up properly, we could copy these lines into our development.rb and test out our S3 uploads on our local server.

Last, but certainly not least, we want to set these configurations up on Heroku. If we don’t do this final step in the process, Heroku won’t know where to look for our credentials, which means that it won’t know where our images are being hosted when it looks inside of our s3_credentials hash!

If we haven’t already, we’ll want to create an application container on Heroku by running this command in the terminal:

1
$ heroku create

And then we’ll want to push our code to our Heroku container and run any pending migrations:

1
2
$ git push heroku master
$ heroku rake db:migrate

These first two steps are only necessary if we’ve never deployed to Heroku before. If we have, then all we really need to do is set our AWS S3 configuration variables:

1
2
3
$ heroku config:set S3_BUCKET_NAME=our_bucket_name
$ heroku config:set AWS_ACCESS_KEY_ID=our_access_key_id
$ heroku config:set AWS_SECRET_ACCESS_KEY=our_secret_access_key

Now the moment of truth: we can run heroku open and see…all of our images live! Next time someone tells us we have to figure out how to safely store our assets, we’ll know that it ain’t no thing. We got this.

tl;dr?

  • Environment variables are how we can store our secret credentials; they live inside of the .env file and should never be committed to source control! Want to read more about env variables in Rails? Check out this great blog post and this super informative tutorial.
  • The aws-sdk gem is an easy way to integrate paperclip and AWS S3. You can dive into the gem’s source code to learn more!
  • Heroku has some fantastic guides on how to set up S3 and how to upload files to S3 using paperclip.