Today I completed the move of the web app for rpglogger from Heroku to Amazon EC2. It was about two days work for this indie developer. I still use Heroku's hosted Postgres database-as-a-service offering for now - in some ways it's pretty great.
Here are the basic steps:
- Make sure the web app is compatible with both the Heroku and the new AWS stack (i.e. you can deploy the identical app in both places)
- Bring up and automate the build process of a new server on AWS (in my case, Ubuntu 10.04 LTS)
- Deploy the app to AWS while it's still live, sharing the database between the two services
- Add new DNS settings for AWS
- Stand down the Heroku dynos
Thinking of moving one of your apps from provider to provider? Here's my story.
Compatibility with both environments:
- Rails 3.2.8
- Heroku Postgres dev database (10k row limit)
- RVM w/Ruby 1.9.2-p320 (MRI)
- Rails 3.2.8
- Same Heroku Postgres dev database
As you can see my software stack is nearly identical. This helped a lot when my first cold deploy to EC2 went successfully and the app was running identically on the new production server.
Why I decided to keep the database where it is
Should I setup a self-provisioned Postgres server in EC2? No, not now. Three main reasons:
- I'm an indie developer when it comes to rpglogger. Can I keep my Postgres instance patched and secured as well as Heroku? Probably not.
- Experience. What if the database experiences some corruption? Do I really want to be the one responsible for fixing that? Nope. Open a ticket with Heroku and let them handle it quickly.
- Cost. Stayng with Heroku hosted solution is free for now - $0/month until I exceed the 10k row limit on the dev plan. After that I can upgrade to the basic plan for only $9/month. No biggie.
Obviously there's no big gain to moving the database at this juncture, introducing a bunch of unknowns, and effectively recuding the robustness and durability of my database - arguably the most critical part of any web app, and one of the most difficult things to recover from should something really go wrong. Like all good SaaS solutions these days you can grow your costs as you grow your use, and there's a free option to get you bootstrapped.
Sidenote on Heroku Postgres security
One, and only one reason I'm a little concerned about keeping my database hosted on Heroku? They expose the server to the internet directly in order to make it accessible by your app. You can literally log into the instance directly with any Postgres client, from any machine.
Am I all that worried? Well yes, this seems to be a bad choice on Heroku's part, and I'm not sure why hosted Postgres doesn't include an interface to setup access lists or (since their infrastructure runs on AWS anyway) a way to restrict connections to servers within AWS only or in some other way close off direct internet access.
Reasons not to worry too much? First, I don't store ANY sensitive data in this app whatsover. I use 3rd party login (Google, Facebook, Twitter) and I don't store any credentials, payment information, or personal details about any user. The data I actually have is video game strategy guide data. Second, there are daily backups that, while I hope I never have to use them, at least make restoring from something catastrophic pretty easy.
What I really hope? Since hosted postgres is a fairly new service, I hope that ACLs and additional security features are coming soon. Honestly I think the greater danger is that, without even knowing your database password, it's possible to access (and destroy) an entire database with all its backups with only a user's Heroku username and password. There's no second factor authentication, so Heroku is basically trusting that its users will follow security best practices when choosing a password. A good, high security password used to be considered enough security. But these days...hm...not so much.
Ultimately, I could move to something like EnterpriseDB, or ask someone I know who is a professional systems admin to help with with setting up and securing my own Postgres server. For now I'm going to see what Heroku comes up with in the next year while keeping my options open.
Moving the app and adding Capistrano support
One of the nicest things about Heroku is how drop-dead simple it is to deploy. No crazy extra steps or gems to install in your app with dependencies to manage. Heroku just takes care of infering what should be in your stack based on what's in your app and configures your deploy target on the fly as you push code via git. Pretty freakin' sweet.
This means that in order to move to AWS I had to find an equivalent, single-command deploy option. I chose Capistrano, the popular and robust Rails deployment tool.
Setting up Capistrano took an afternoon if you count all the time from the `capify` command to getting the tasks setup the way I wanted. Not too bad. What actually took even longer than that was building, testing, customizing, re-testing, and finally settling on a bootstrap and package dependency install script that would allow me to build a production server from a blank Ubuntu 10.04 LTS image. That probably took another day and a half. So, two whole days to replace the heroku deploy toolchain with something that is nearly as simple to use now that it's up and running.
Another side note: creating a staging server
One major upside to moving to AWS (though I could have done this with Heroku as well, I suppose) is the addition of a staging server to my deployment pipeline to help catch bugs before they hit production. In my case my staging server fits nicely inside a micro instance for about $5-6/month. My two days of Capistrano and shell scripting work paid off here because once I had my staging server built it meant that I could build my new production server in a consistent, automated fashion from a blank OS image within 40 minutes. I'd like to shave that down to somewhere in the 5-10 minute range at some point, but it's not a huge priority at this point.
DNS and connecting the database to the AWS server
Once I had my production server up and running, all that was really left was to setup the database.yml file on the new EC2 instance and point it to the Heroku Postgres database that was already connected to the old Heroku web app. This was as simple as logging into Heroku Postgres and copying the connection settings over to my EC2 server.
After that was working, and I was sure that things were good to go in EC2, all that was left was to swap the DNS settings. This only took a few minutes where I replaced the old A records pointing rpglogger.com to Heroku with my new Elastic IP address on AWS. Because I had exactly the same version of the app running on both Heroky and AWS, and because they both point to the same backend database, Heroku was essentially acting as a second production app server as the changes to the DNS records get propagated across the internet. This is basically a zero-downtime migration for me, and that's great!
I'm using the dev plan on Heroku Postgres (10k row limit). At some point in the not too distant future I'm going to hit that limit and need to migration to the basic plan. I'll cross that bridge when I come to it.