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 |
|
This means that we can safely put our important AWS credentials inside of our .env
file:
1 2 3 4 |
|
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 |
|
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 |
|
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
|
|
And then we’ll want to push our code to our Heroku container and run any pending migrations:
1 2 |
|
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 |
|
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 integratepaperclip
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.