Writing software is awesome, but it’s only part of the picture. We have to deploy that software to make it accessible to the world. Until now, I’d always used Heroku — it took all of 30 seconds to deploy my applications, but I felt like I was missing parts of the picture. So today I deployed an app manually using Vagrant and Digital Ocean. I can now say with confidence that I love writing software more than I like dealing with infrastructure, but that infrastructure is interesting too, and that Heroku is an incredibly powerful tool. Huge thanks go out to my husband for this one — he actually does operations for a living, and was able to make it accessible to me.
Digital Ocean provides virtual servers for software developers. It’s about $10/month, depending on what you’re using it for. (If you sign up through this link, you’ll get $10 credit on DigitalOcean. Full disclosure — I’ll get $25)
Vagrant allows you to configure your environment (the server hosted on Digital Ocean)
- Get an API key from Digital Ocean
- brew cask install vagrant
- vagrant plugin install vagrant-digitalocean — documentation for that here
In the root directory of my rails app, created a file called Vagrantfile. It looked like this:
Vagrant.configure('2') do |config|
config.vm.hostname = 'wandermap'
config.vm.provider :digital_ocean do |provider, override|
override.ssh.private_key_path = '~/.ssh/id_rsa'
override.vm.box = 'digital_ocean'
override.vm.box_url = "https://github.com/devopsgroup-io/vagrant-digitalocean/raw/master/box/digital_ocean.box" provider.token = Secret.digital_ocean_key
provider.image = 'ubuntu-14-04-x64'
provider.region = 'sfo1'
provider.size = '1gb'
config.vm.provision :shell, path: 'install.sh'
Let’s walk through that.
Line 2 sets the hostname and the name of my DigitalOcean Droplet
Most of the rest I just grabbed from the DigitalOcean Vagrant Provider linked to above. I used Vagrant-Secret to store my DigitalOcean API key to avoid committing it to GitHub (line 8)
I’m using their datacenter in San Francisco, because it’s the closest one to Denver, but you may want to change that depending on where you’re located (line 10)
I started off trying to use 512mb of RAM, but ran out of space, so ended up moving up to 1gb (line 11). Your needs may be higher or lower.
And then the magic happens. Line 14 basically says that when you run vagrant up or vagrant provision, to run the script that is in the install.sh file, also located in the root directory of my app. It’s written for shell, but this is where you could use something like Ansible or Chef or Puppet if you were so inclined. Here’s what that file looked like:
#!/bin/bash sudo apt-get install -y software-properties-common build-essential libpq-dev postgresql git nodejs imagemagick
sudo apt-add-repository ppa:brightbox/ruby-ng
sudo apt-get update
sudo apt-get install -y ruby2.2 ruby2.2-dev gem install bundler bundle install --gemfile=/vagrant/Gemfile
sudo -u postgres psql -1 -c "CREATE USER db_service WITH PASSWORD '(put password here)';"
sudo -u postgres psql -1 -c "ALTER USER db_service WITH SUPERUSER;"
rake --rakefile=/vagrant/Rakefile db:create db:migrate
cd /vagrant ip="$(ifconfig | grep -A 1 'eth0' | tail -1 | cut -d ':' -f 2 | cut -d ' ' -f 1)"
rails s -p 80 -d --binding=$ip
A lot of this are things you’ve likely done on your local machine and forgotten about, but since you’re basically setting up a new server, it requires a lot of installations that your app is dependent on.
apt-get is the official way to install packages on Ubuntu. The -y flag prevents you from having to respond to Are you sure? [Y/n] queries while the script runs.
Line 2 installs:
- Software Properties Common: a package that allows you to manage repositories. This becomes relevant for Brightbox, below.
- Build Essential: This contains the C and C++ compilers that Rails Requires
- libpq-dev: PostgreSQL requires this
- Postgres: what I’m using for my database
- Git: I had some gems in my gemfile downloaded directly from Git
- Node: A dependency for one of my gems.
- Imagemagick: A dependency for using Paperclip/AWS to upload images
Brightbox: A third party repo that maintains repositories containing all the versions of Ruby.
Line 5 installs the specific version of Ruby this app was developed for, using Brightbox — for obvious reasons
Line 7 installs bundler — also obvious. It’s a Rails app.
Those were all the dependencies for my app and gems, but yours may well have others.
bundle — once again, it’s a Rails app. I did have to specify the path to the Gemfile, since Vagrant doesn’t automatically run from within the app
Lines 10 and 11 create a user with permissions to create and migrate the database through Postgres
Line 12 creates and migrates the database, once again having to specify the path to the Rakefile
This next piece is actually super interesting. In order to run the server from an ip address other than localhost, you need to specify the IP address for it to bind to. The problem here is that your unique IP address isn’t generated until the DigitalOcean Droplet is created, which has already happened by the time the script reaches this point, but in order to fully automate the process (the whole point), you need to go in and find it. Line 14 goes in and sets a bash variable equal to that IP address. Line 15 then starts up the server with the proper binding, on port 80. the -d means it runs in the background.
The first time you’re creating your app, run vagrant up. This will spin up your server, Digital Ocean Droplet, put your code onto the server, and run all of the commands in your install.sh script.
For future deployments, you can run vagrant provision. This is imperfect, as it will do everything in the script, regardless of it already being there. For example, your server will already have Ruby on it, but it will go through that process again. I have some work to do on improved automation!
If for some reason you need to tear down your server completely, either because you don’t want it anymore, or because you need to start over, you can run vagrant destroy.
I have a domain hosted on DreamHost, and was able to add an A name pointing to the IP address that DigitalOcean provided. Your process may be different depending on your domain setup. If you destroy and re-create your Vagrant Box, your IP address will change, so you’ll need to update this as well.