Deploying a Rails app to Digital Ocean with Capistrano
Drew Bragg
Posted on October 4, 2018
This is the basic steps on how I setup my DO servers and deploy my rails apps. I originally learned this from the GoRails screencast but have been iterating over it and tweaking/improving it over time. Maybe this write up will help you get your apps live or maybe you have a better way you can share with me.
If you don't have a Digital Ocean account, you can sign up using this link, https://m.do.co/c/33c1f4fb065a. Yes I get a referral credit if you use my link but only if you end up spending money with them, even if you dont you still get $100 in credits for 60 days if you sign up during Hacktoberfest.
Lets spin up a server!
Creating a droplet
-
Create a droplet
- Choose Ubuntu as your distribution (16.04.4 x64)
- Choose the size of your droplet (min 1GB and 1CPU)
- Choose New York 1 or 3 for your datacenter (I'm on the East Coast so this makes the most sense for me)
- Optional:
- Add SSH keys now (can also do this later)
- Rename the hostname (generally to something that makes sense)
Once the droplet is created an email is sent with the login credentials
Use the provided credentials to SSH into the new droplet
-
It is best practice to make a new user to manage deployments rather that using
root
$ sudo adduser deploy
$ sudo adduser deploy sudo
$ su deploy
If you did not provide SSH keys when you created the droplet you should do that now. If you did, skip this:
Do this on your computer NOT on the server!
I use a MacOS tool called ssh-copy-id. If you don't have it you can get it by running $ brew install ssh-copy-id
* Run $ ssh-copy-id deploy@SERVERIP
where SERVERIP is the IP address of your droplet
Back to our regularly scheduled program
- SSH back into the server as the deploy user
Run the following on the server as deploy
-
First get Node and Yarn.
$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
-
Then install you basic dependencies.
$ sudo apt-get update
$ sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev nodejs yarn
-
Then install rbenv (for managing our ruby versions, you can use rvm if you want but I'm partial to rbenv)
$ cd
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
$ exec $SHELL
$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
$ echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
$ exec $SHELL
-
Finally install ruby
-
$ rbenv install 2.4.1
-- or whatever version of ruby your application is running -
$ rbenv global 2.4.1
-- or whatever you just installed -
$ ruby -v
-- to verify the correct version is installed and set $ gem install bundler
$ rbenv rehash
-
Setup Nginx with Passenger
This will actually run our 'server' and serve (no pun intended) our app
$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
$ sudo apt-get install -y apt-transport-https ca-certificates
$ sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger xenial main > /etc/apt/sources.list.d/passenger.list'
$ sudo apt-get update
$ sudo apt-get install -y nginx-extras passenger
Check to make sure everything you just did worked correctly
Run $ sudo service nginx start
on the server and then visit the droplets IP address in the browser
If you did everything right so far you should see an Nginx Welcome message.
Configure Nginx and Passenger
-
$ sudo vim /etc/nginx/nginx.conf
- Find the following lines of code and uncomment
include /etc/nginx/passenger.conf
:## # Phusion Passenger ## # Uncomment it if you installed ruby-passenger or ruby-passenger-enterprise ##
- Find the following lines of code and uncomment
include /etc/nginx/passenger.conf;
-
Save and close (:wq)
$ sudo vim /etc/nginx/passenger.conf
-
Change the passenger_ruby line to point to rbenv (or rvm if you used that):
passenger_ruby /home/deploy/.rbenv/shims/ruby;
Save and close (:wq)
Once this is all completed restart the Nginx Server
$ sudo service nginx restart
Environmental Variables
- Ensure you are in the top most level of the deploy user section
- Run
$ cd ~
- Run
$ git clone https://github.com/rbenv/rbenv-vars.git $(rbenv root)/plugins/rbenv-vars
$ vim .rbenv-vars
- Add any Environmental Variables you need to the file.
PostgreSQL
- Run
$ sudo apt-get install postgresql postgresql-contrib libpq-dev
- Set up the postgres user (also named "deploy" but different from our linux user named "deploy") and DB
$ sudo su - postgres
-
$ createuser --pwprompt deploy
-- make note of the password you choose -
$ createdb -O deploy APP_NAME_production
-- where APP_NAME is the name of the application $ exit
Deploying
First we need to configure our repo's bucket to be able to talk to our server. Since we deploy directly from the repo rather that our local machine.
- While SSHed in our server, enter
ssh-keygen
at the command line. - Follow the prompts to set up a new key. A passphase is not needed in this case.
- Once the server has generated a new key run
cat ~/.ssh/id_rsa.pub
and copy the returned key - Add the key to the App's repo
- If you're using bitbucket that can be found in settings > access keys
Back on your local machine let's set up capistrano
-
In your Gemfile add:
gem 'capistrano'
gem 'capistrano-rails'
gem 'capistrano-passenger'
gem 'capistrano-rbenv' (if you used rvm use: gem 'capistrano-rvm') Run
$ bundle install
and then$ cap install
If you want to customize this install consult the capistrano docs.
-
In
config/deploy.rb
change the following lines, updating them to the name and repo of the current application:set :application, "YOUR_APPS_NAME" set :repo_url, "YOUR_APPS_REPO" set :deploy_to, "/home/deploy/YOUR_APPS_NAME" append :linked_files, "config/database.yml", "config/secrets.yml" append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system"
-
In
config/deploy/production.rb
change the IP address of the server to the IP of the new dropletserver '104.131.189.221', user: 'deploy', roles: %w{app db web}
-
You can then use the command
cap production deploy
to deploy new versions of the app- The first time you try this it will fail and throw an error, but we're almost there.
Home Stretch
SSH back into the server as deploy and create 2 files
-
/home/deploy/APP_NAME/shared/config/database.yml
- In this file add the following:
production: adapter: postgresql host: 127.0.0.1 database: APP_NAME_production username: deploy password: YOUR_POSTGRES_PASSWORD (NOT YOUR LINUX PASSWORD) encoding: unicode pool: 5
- In this file add the following:
-
/home/deploy/APP_NAME/shared/config/secrets.yml
- In this file add the following:
production: secret_key_base: YOUR_SECRET_KEY
(Note: I'm fairly certain there is a better way to do this in Rails 5.2 but I haven't personally used that in production yet)
- In this file add the following:
Run $ sudo visudo -f /etc/sudoers.d/nginx_overrides
and add the following to the file:
# Nginx Commands Cmnd_Alias NGINX_RESTART = /usr/sbin/service nginx restart Cmnd_Alias NGINX_RELOAD = /usr/sbin/service nginx reload # No-Password Commands deploy ALL=NOPASSWD: NGINX_RESTART, NGINX_RELOAD
Updating the URL
Last this to do on the server is to tell Nginx to respond with the app
- Run
sudo ln -s /etc/nginx/sites-available/THE_URL_YOU_WANT_HERE.com /etc/nginx/sites-enabled/
-
Then
sudo vim /etc/nginx/sites-enabled/THE_URL_YOU_WANT_HERE.com
and replace the files contents with:server { listen 80; listen [::]:80 ipv6only=on; server_name THE_URL_YOU_WANT_HERE.com; passenger_enabled on; rails_env production; root /home/deploy/APP_NAME/current/public; # redirect server error pages to the static page /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } }
Back on your local machine run
$ cap production deploy
once more and then you should be in businessIf you are still seeing the default Nginx page, run
$ cap production server:restart
If I missed anything or there's something I did wrong (or could do better) please let me know. Obviously there's a lot more to running a 'real' production server, logs, security, load balancing, etc, but this will get your app out into the world. Plus it feels pretty cool to configure your own server.
Posted on October 4, 2018
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.