Intro
In this article, we will deploy our Ruby on Rails application on a Ubuntu 18.04 server with Nginx (web server) and Puma (application server). We will also use PostgreSQL as our database and Rbenv to help us store sensitive information. Rbenv helps us store sensitive information (such as database passwords) on the server as environment variables. This way if your project is on Github as a public project, you won’t have to reveal that kind of information to the world.
If you have created a brand new server or VM, remember to install all the dependencies that our project needs. I have created a script to help with this (installing rails, ruby, git, etc) in case you need here: https://raw.githubusercontent.com/mt9304/scripts/master/rails.sh. This script can take a while to complete, but you can download and run this on your new server by running: tent/uploads/nginx-logo-e1548640254225.pngIf you have created a brand new server or VM, remember to install all the dependencies that our project needs. I have created a script to help with this (installing rails, ruby, git, etc) in case you need here: https://raw.githubusercontent.com/mt9304/scripts/master/rails.sh. This script can take a while to complete, but you can download and run this on your new server by running:
sudo apt install curl
curl -o https://raw.githubusercontent.com/mt9304/scripts/master/rails.sh rails.sh
sh rails.sh
Installing Dependencies
After our environment is setup, let’s install more dependencies!
sudo apt update
sudo apt-get install autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev
cd ~/.rbenv/plugins
git clone https://github.com/sstephenson/rbenv-vars.git
Now let’s go back to our home directory and clone our project folder here.
cd ~
(Remember to replace [YOUR_USER] and [YOUR_PROJECT_NAME] with the values that match your own project Github. In my case, it would be: https://github.com/TestingAndLearning/rails_template)
git clone https://github.com/[YOUR_USER]/[YOUR_PROJECT_NAME]
Now change directory into your application’s folder and run:
sudo apt-get install libsqlite3-dev
bundle install
Note:
If you don’t install libsqlite3-dev and your application references sqlite, then you may get an error like:
An error occurred while installing sqlite3 (1.3.13), and Bundler cannot continue.
Make sure that `gem install sqlite3 -v ‘1.3.13’` succeeds before bundling.
Configuration Changes
Now we will go into the /config/database.yml file and make some changes to the Production environment, since this is the environment we will be deploying in (Remember to replace the values in quotes with your own).
adapter: postgresql encoding: unicode database: RT_prod pool: 5 username: <%= ENV['NAME_OF_YOUR_USER'] %> password: <%= ENV['YOUR_DATA_BASE_PASSWORD'] %>
Your file might look something like this: https://gist.github.com/mt9304/2c8014ffed16adfe072970050f8643ae
These <%= ENV[‘VALUE’] %> tags are the environment variables that Rbenv will use.
Now run the following to get a unique value for your application:
rake secret
Make sure to copy this down somewhere. Now we can create our rbenv file and add these values:
nano .rbenv vars or any other text editor like vi or vim. Then edit the contents in this file to be something like: SECRET_KEY_BASE=YOURRAKESECRETOUTPUT NAME_OF_YOUR_USER=YOURACTUALUSER YOUR_DATA_BASE_PASSWORD=YOURACTUALPASSWORD
Remember to replace the values with your own. These should match the ones you entered in the database.yml file. For the user value, I would recommend using the value you see when you run:
echo $USER
This way you can avoid some permission issues with the database in the future.
Git Ignore
Also, if you haven’t already, let’s create a .gitignore file and make sure we ignore this file so that it doesn’t end up in the git repository. In the application folder, create the file by running:
nano .gitignore
Then make sure one of the lines says
.rbenv-vars
Save the file and run the following to make sure the .gitignore works properly.
git rm -r --cached .
Now you can push into your repository and test if the .rbenv-vars file ends up in there.
More Configurations
Run the following command and remember the number it outputs (this gets the number of CPUs available to use in your server):
grep -c processor /proc/cpuinfo
We can now edit our /config/puma.rb file. Remove the contents in there and make sure it looks something like this: https://gist.github.com/mt9304/7a444e84098e49ce5d0f92e263f95e21
Remember to change the YOURCPUOUTPUT value to the number of CPUs we just found.
Now let’s go back to your application’s root directory and run the following to create some required folders:
mkdir -p shared/pids shared/sockets shared/log
For the next bit, we will use some scripts I found on the internet that will allow you to start/stop the Puma server easily. We won’t go into this in too much detail, but feel free to go to https://www.digitalocean.com/community/tutorials/how-to-deploy-a-rails-app-with-puma-and-nginx-on-ubuntu-14-04 for a similar guide. Go back to your home directory and download the scripts by running this:
cd ~ wget https://raw.githubusercontent.com/puma/puma/master/tools/jungle/upstart/puma-manager.conf wget https://raw.githubusercontent.com/puma/puma/master/tools/jungle/upstart/puma.conf sudo cp puma.conf puma-manager.conf /etc/init
Make sure to edit the YOURUSER and YOURAPPLICATIONNAME values so that it leads to your application folder.
cat << EOF | sudo tee -a /etc/puma.conf /home/YOURUSER/YOURAPPLICATIONNAME EOF
Note:
The “cat” command here helps us write a line, the “tee” command takes the output of what we write and writes it in the /etc/puma.conf file. The -a argument makes sure it appends to the end of the file so that it doesn’t overwrite anything. The “<< EOF” part tells the “cat” command to take anything in the next lines as output for this, until the word EOF is found on its own line. In ths case, it is the path to your application’s directory. Then the EOF at the end is detected and it stops writing as output.
Puma Configuration
For your convenience, you can run the following to set your variables in this session (Make sure to replace appfoldername with your application’s folder name. Also, if you are using the default user, then you don’t have to set this. You can check the values of these by typing echo $USER or echo APP_NAME once you have set them):
USER=ubuntu APP_NAME=appfoldername
Now we can run the following
cat << EOF | sudo tee /etc/systemd/system/puma.service #Modify the path to the app directory and the user and group. [Unit] Description=Puma HTTP Server After=network.target # Uncomment for socket activation (see below) # Requires=puma.socket [Service] # Foreground process (do not use --daemon in ExecStart or config.rb) Type=simple # Preferably configure a non-privileged user User=${USER} Group=${USER} # Specify the path to your puma application root WorkingDirectory=/home/${USER}/${APP_NAME} # Helpful for debugging socket activation, etc. # Environment=PUMA_DEBUG=1 # EnvironmentFile=/home/${USER}/${APP_NAME}/.env # The command to start Puma # ExecStart=/sbin/puma -b tcp://0.0.0.0:9292 -b ssl://0.0.0.0:9293?key=key.pem&cert=cert.pem # ExecStart=/usr/local/bin/bundle exec --keep-file-descriptors puma -e production # ExecStart=/usr/local/bin/puma -C /home/${USER}/${APP_NAME}/config/puma.rb ExecStart=/home/${USER}/.rbenv/shims/bundle exec puma -e production -C ./config/puma.rb config.ru PIDFile=/home/${USER}/${APP_NAME}/shared/tmp/pids/puma.pid Restart=always [Install] WantedBy=multi-user.target EOF
Then restart and enable Puma.
sudo systemctl daemon-reload sudo systemctl enable puma
Nginx
Almost done, now we can finally install Nginx:
sudo apt-get install nginx
And we can replate some default files with our own by running:
sudo rm /etc/nginx/sites-available/default
cat << EOF | sudo tee /etc/nginx/sites-available/default upstream app { # Path to Puma SOCK file, as defined previously server unix:/home/${USER}/${APP_NAME}/shared/sockets/puma.sock fail_timeout=0; } server { listen 80; server_name localhost; root /home/${USER}/${APP_NAME}/public; try_files $uri/index.html $uri @app; location @app { proxy_pass http://app; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header Host \$http_host; proxy_redirect off; } error_page 500 502 503 504 /500.html; client_max_body_size 4G; keepalive_timeout 10; } EOF
PostgreSQL Database
Now let’s create the users for our database and give it the Superuser role. In the below steps, we basically change to use the postgres user, then enter the pg console with psql, which allows us to run SQL commands for our database. Make sure to replace the user and password values with your own, and include the semi colon at the end:
sudo -iu postgres psql CREATE USER username WITH PASSWORD 'yourpassword'; "ALTER USER username WITH SUPERUSER; \q exit
Note:
“\q” exits you from psql and “exit” takes you back into the default user. While in psql you can also type “\l” to list all the databases, “\c databasename” to connect to a database then \dt to list all the tables in that database. You can then run SQL commands such as “SELECT * FROM tablename;” to interact with your database.
The below commands also accomplishes the same thing in case you just want to save time (replace the YOURPASSWORD value with your password):
DB_PASS=YOURPASSWORD sudo -u postgres bash -c "psql -c \"CREATE USER $USER WITH PASSWORD '$DB_PASS';\"" sudo -u postgres bash -c "psql -c \"ALTER USER $USER WITH SUPERUSER;\""
Now that we have the users avaiable for the database, we can set up the Production environment’s database. Change directory into your application’s folder and run the following (make sure your Gemfile has the pg gem in it. There should be a line that says something like gem ‘pg’, ‘~> 0.18’:
RAILS_ENV=production rake db:drop RAILS_ENV=production rake db:create RAILS_ENV=production rake db:migrate RAILS_ENV=production rake db:seed RAILS_ENV=production rake assets:precompile
Restarting the Server
Finally, we can restart Puma and Nginx and for the changes to take affect:
sudo systemctl stop puma sudo systemctl start puma
or
sudo systemctl restart puma
sudo systemctl stop nginx sudo systemctl start nginx
or
sudo systemctl restart nginx
You can run the following to check the status of these servers to make sure that they are action and running:
sudo systemctl status nginx sudo systemctl status puma
Some of those scripts that we downloaded and used above allow for Puma to be interacted with the systemctl command.
We can now visit localhost:80 to check the web application. In development, you would usually visit a Rails application through port 3000, however, Nginx makes it possible to connect it through port 80, which is the default port for Production websites. This essentially means you can just type localhost in the URL without the port number and it should take you to the site. This makes it easier to point your domains and such to your server when you decide to go public with your website.
Note:
If you find that your CSS and JavaScript files are not loading, you can open the /config/environments/production.rb file and change the following line to true, then restart Puma.
config.public_file_server.enabled = true
Your website should now be ready to go public!
Navigation
The next article can be found here. Previous article is here.