Sinatra is a concise framework that does not like to show off. It gets the job done, without forcing anything on the developer. When it comes to taking your application, developed using this wonderful little tool, the same logic applies and you are welcomed with multiple choices. Each working pretty much the same way: getting the job done without unnecessary complexities.
In this DigitalOcean article, following our first Ruby 2.1 & Sinatra On Ubuntu 13 tutorial, we will learn a couple of different ways to take your Sinatra application out of its hidden box and share it with the world [on Ubuntu] using different (and interesting) technologies and methods.
Note: This article’s examples build on our first Sinatra & Ruby 2.1.0 on Ubuntu 13 article by convention. If you would like to learn more about getting started with the framework or how to prepare the operating system with Ruby 2.1.0 and Sinatra, consider checking it out before continuing with this piece.
Deploying an application (regardless of it being a web site, an API or a server) usually means setting up a system from scratch (or from a snapshot taken in time), preparing it by updating everything, downloading dependencies, setting up the file structure and permissions followed by finally uploading your codebase or downloading it using a source control manager (SCM) such as Git.
Following the design incentives of Sinatra, we are going to try to keep things as simple and possible and go with tried, tested, and easy to use methods for our deployment examples here. We will be working with reputable and trusted tools to handle the job and learn about their differences.
The term “application server” applies to applications (i.e. servers), which (usually) contains another application (e.g. your web-application) and following certain specifications and interfaces (i.e. a common language), allows the contained application to communicate with the outside world.
These tools are, again, usually geared towards handling the business logic (performing procedures) alone and not for other HTTP / WWW operations such as sending or receiving static file assets, dealing with multiple clients, or contending with long standing connections – although, thanks to many readily available libraries, there are such application servers available as well.
In most set-ups, one or more application servers (e.g. Passenger, Unicorn, Puma, Thin etc.) are placed behind a proper HTTP / WWW server (e.g. Nginx, Apache etc.), tasked with handling and dealing with all incoming connections first, before passing it to the next level. This allows serving of assets (e.g. javascript files, images etc.) to be extremely efficient, meanwhile leveraging the capabilities of both applications to their maximum and keeping clients on-the-line (i.e. not dropping connections) and processing requests within the application layer.
Note: To learn about different Ruby web-application servers and understand what Rack is, check out our article A Comparison of (Rack) Web Servers for Ruby Web Applications.
Rack middleware, implementing the Rack specification, works by dividing incoming HTTP requests into different pipelined stages and handles them in pieces until it sends back a response coming from your web application (controller). It has two distinct components, a Handler and an Adapter, used for communicating with web servers and applications (frameworks) respectively.
In regards to Ruby based frameworks, web-application servers do their job by implementing the Rack specification / interface and connecting to your application through config.ru
file, calling (and importing) your application as an object.
Passenger is a mature, feature rich product which aims to cover necessary needs and areas of application deployment whilst greatly simplifying the set-up and getting-started procedures. It eliminates the traditional middleman architecture by directly integrating with the Nginx (and Apache) reverse-proxy.
This highly popular tool can be used widely in many production scenarios. The open-source version of Passenger (which is what we will be using) has a multi-process single-threaded operation mode. Its Enterprise version can be configured to work either single-threaded or multi-threaded, depending on your needs.
To learn more about Passenger, you can visit its official website located at https://www.phusionpassenger.com/.
Unicorn is a very mature web application server for Ruby/Rack based web applications. It is fully-featured; however, it denies by design trying to do everything: Unicorn’s principal is doing what needs to be done by a web application server and delegating the rest of the responsibilities (e.g. the operating system).
Unicorn’s master process spawns workers, as per your requirements, to serve the requests. This process also monitors the workers in order to prevent memory and process related staggering issues. What this means for system administrators is that it will kill a process if (for example) it takes too much time to complete a task or memory issues occur.
As mentioned above, one of the areas in which Unicorn delegates tasks is using the operating system for load balancing. This allows the requests not to pile up against busy workers spawned.
Apache is an HTTP server that does not really need any introduction at this point. It has been world’s most popular HTTP server and it is an extremely mature, feature rich and highly configurable product that runs and works extremely well. In this article, one of the front-facing servers we will work with and use is Apache and we will see how it integrates with the Phusion Passenger application server.
Nginx is designed from ground up to act as a multi-purpose HTTP server. It is capable of serving static files (e.g. images, text files etc.) extremely well, balance connections and deal with certain exploits attempts. It acts as the first entry point of all requests, and passes them to Passenger for the web application to process and return a response.
It is a very high performant web server / (reverse)-proxy that is relatively easy to work with and easy to extend (with add-ons and plug-ins). Thanks to its architecture, Nginx is capable of handling a lot of requests (virtually unlimited), which - depending on your application or website load - could be really hard to tackle using some other, older alternatives.
Remember: “Handling” connections technically means not dropping them and being able to serve them with something. You still need your application [server] and database functioning well in order to have Nginx serve clients’ responses that are not error messages.
To learn more about Nginx, you can visit its official website located at nginx.com.
After deciding on which server combination you would like to work with, the next step consists of actually getting them installed and ready on the droplet you want your application to run.
Note: Before installing Apache and Passenger, make sure to disable (or remove) Nginx if you have it installed; or configure it to not to block Apache’s way (i.e. port / socket collisions).
Note: If your droplet has less than 1 GB of RAM, you will need to perform the below simple procedure to prepare a SWAP disk space to be used as a temporary data holder (i.e. RAM substitute) for Passenger. Since DigitalOcean virtual servers come with fast SSD disks, this does not really constitute an issue whilst performing the server application installation tasks.
# Create a 1024 MB SWAP space
# The process should complete within less than a minute
sudo dd if=/dev/zero of=/swap bs=1M count=1024
sudo mkswap /swap
sudo swapon /swap
We will begin getting the Apache and Passenger combination with first preparing the system with the tools that Passenger requires.
These tools consist of the following:
Curl development headers with SSL support
Apache 2
Apache 2 development headers
Apache Portable Runtime (APR) development headers
Apache Portable Runtime Utility (APU) development headers
Let’s get them one by one, following the official Passenger documentations’ suggested ways:
# Install cURL development headers with SSL support:
apt-get install libcurl4-openssl-dev
# Apache 2:
apt-get install apache2-mpm-worker
# Apache 2 development headers:
apt-get install apache2-threaded-dev
# And finally, Apache PRU (APU) development headers:
apt-get install libapr1-dev
Next, let’s download and install Passenger using RubyGems’ gem
:
gem install passenger
# This method of installing Passenger will use
# the available and activated Ruby interpreter.
# However, the installation will be available for
# all Ruby versions' use.
Once we have all the necessary elements ready, we can get the glue layer (i.e. module) that will allow Apache and Passenger to couple and work together.
Begin the installation procedure by running the following command:
passenger-install-apache2-module
# Here's what you can expect from the installation process:
# 1. The Apache 2 module will be installed for you.
# 2. You'll learn how to configure Apache.
# 3. You'll learn how to deploy a Ruby on Rails application.
# ..
Press enter to continue.
Now the installer will ask you to choose which programming languages you will be working with. Scroll down with your arrow keys and use the space bar to choose what’s appropriate for your use. Since we are aiming to deploy Sinatra, our selection will cover Ruby alone.
Which languages are you interested in?
Use <space> to select.
If the menu doesn't display correctly, ensure that your terminal supports UTF-8.
‣ ⬢ Ruby
⬡ Python
⬡ Node.js
⬡ Meteor
Once you have made your choice, press enter to advance to the next step.
Now the installer will start compiling the Apache module.
Note: This bit might take a short while – usually a couple of minutes.
Note: Before installing Nginx with Unicorn, make sure to disable (or remove) Apache, or configure it to not to block Nginx (i.e. port / socket collisions).
Note: If you are constrained with system resources (e.g. amount of RAM you have available), you might want to choose this combination to deploy your application.
Nginx and Unicorn together make a great combination for web-application deployments. Unicorn, as we have discussed previously, is an amazing server that works in smart ways and make user of all available system tools to handle the job – and it does it well!
We will start the installation process by getting Nginx first. The default system package manager aptitude
(or apt-get
) will be able to download and install Nginx for us.
Run the following to download and install Nginx using aptitude
:
aptitude install nginx
Once we have Nginx ready, the next step consists of getting Unicorn web-application server, which is made easy by RubyGems package manager.
Run the following to download and install Unicorn using gem
:
gem install unicorn
In this section, we are going to see how to configure both server combinations to get our applications online. You should remember that despite the marketing efforts, you will be able to deploy any number of web-applications as long as you configure them properly and have enough system resources to support them.
Note: Remember that you should choose one-or-the-other from above web-application / HTTP server combinations before continuing with (and applying) the below settings.
After finishing installing Apache, Passenger and Passenger’s Apache module, you will be shown a message titled Almost there!
Please edit your Apache configuration file and add these lines:
LoadModule passenger_module /usr/local/rvm/gems/ruby-2.1.0/gems/passenger-4.0.37/buildout/apache2/mod_passenger.so <IfModule mod_passenger.c> PassengerRoot /usr/local/rvm/gems/ruby-2.1.0/gems/passenger-4.0.37 PassengerDefaultRuby /usr/local/rvm/gems/ruby-2.1.0/wrappers/ruby </IfModule>
After you restart Apache, you are ready to deploy any number of web applications on Apache, with a minimum amount of configuration!
So, as suggested and advised, let’s add the configuration block to our Apache configuration file.
Run the following to edit the Apache configuration file using nano text editor:
nano /etc/apache2/apache2.conf
Add the below block of text to where you see fit, without affecting other configurations:
LoadModule passenger_module /usr/local/rvm/gems/ruby-2.1.0/gems/passenger-4.0.37/buildout/apache2/mod_passenger.so
<IfModule mod_passenger.c>
PassengerRoot /usr/local/rvm/gems/ruby-2.1.0/gems/passenger-4.0.37
PassengerDefaultRuby /usr/local/rvm/gems/ruby-2.1.0/wrappers/ruby
</IfModule>
Save and exit by pressing CTRL+X and confirming with Y.
Note: The above configuration must be kept as is. Its parameters are defined in a way to use the relevant Ruby version (2.1.0
installed during the first Sinatra tutorial) at the relevant location (/usr/local/rvm/gems/ruby-2.1.0/..
).
Next, we need to define an application as suggested by the installer:
Deploying a web application: an example
Suppose you have a web application in /somewhere. Add a virtual host to your Apache configuration file and set its DocumentRoot to /somewhere/public:
<VirtualHost *:80> ServerName www.yourhost.com # !!! Be sure to point DocumentRoot to ‘public’! DocumentRoot /somewhere/public
<Directory /somewhere/public> # This relaxes Apache security settings. AllowOverride all # MultiViews must be turned off. Options -MultiViews </Directory> </VirtualHost>
You can deploy yours anywhere you like; however, what can be considered a good example is using either:
A sub-directory of a user, set for deployments, or;
Using the general /var/www
location, as we have done in the past.
Note: For this next step of configurations, we will use our sample application from the previous Sinatra/Ruby 2.1.0 tutorial.
To let Apache know about our application and get Passenger to run it, we need to define a virtual host. For this purpose, we will create a file inside the /etc/apache2/sites-available
directory.
Let’s create an empty configuration file called my_app
using nano:
Note: You can name this file as you see fit to match your application settings.
nano /etc/apache2/sites-available/my_app.conf
Place the contents below, modifying them to suit your application deployment directory:
<VirtualHost *:80>
# ServerName [!! Your domain OR droplet's IP]
ServerName 162.243.74.190
# !!! Be sure to point DocumentRoot to 'public'!
# DocumentRoot [root to your app./public]
DocumentRoot /var/www/my_app/public
# Directory [root to your app./public]
<Directory /var/www/my_app/public>
# This relaxes Apache security settings.
AllowOverride all
# MultiViews must be turned off.
Options -MultiViews
</Directory>
</VirtualHost>
Save and exit by pressing CTRL+X and confirming with Y.
Now we can add our new site configuration to Apache:
# Usage sudo a2ensite [configuration name without .conf extension]
sudo a2ensite my_app
And reload:
service apache2 reload
service apache2 restart
Your web-application should now be alive and on-line. Visit your droplet’s IP address or the domain you have redirected and defined under the ServerName
configuration:
http://162.243.74.190/
# Hello world!
Note: If you have opted for working with a domain name, you should add it to your /etc/hosts
file as well with the following command:
nano /etc/hosts
And append your domain to the list:
# 127.0.0.1 [domain.tld]
# 127.0.0.1 [www.domain.tld]
127.0.0.1 example.com
127.0.0.1 www.example.com
Unicorn can be configured a number of ways. For this tutorial, focusing on the key elements, we will create a file from scratch which is going to be used by Unicorn when starting the application server daemon.
Open up a blank unicorn.rb
document, which will be saved inside the application directory (/var/www/my_app
) directory:
nano unicorn.rb
Place the below block of code, modifying it as necessary:
# Set the working application directory
# working_directory "/path/to/your/app"
working_directory "/var/www/my_app"
# Unicorn PID file location
# pid "/path/to/pids/unicorn.pid"
pid "/var/www/my_app/pids/unicorn.pid"
# Path to logs
# stderr_path "/path/to/logs/unicorn.log"
# stdout_path "/path/to/logs/unicorn.log"
stderr_path "/var/www/my_app/logs/unicorn.log"
stdout_path "/var/www/my_app/logs/unicorn.log"
# Unicorn socket
# listen "/tmp/unicorn.[app name].sock"
listen "/tmp/unicorn.myapp.sock"
# Number of processes
# worker_processes 4
worker_processes 2
# Time-out
timeout 30
Save and exit by pressing CTRL+X and confirming with Y.
Note: To simply test your application with Unicorn, you can run unicorn
inside the application directory.
Next, we need to tell Nginx how to talk to Unicorn. For this purpose, it is sufficient at this level to edit the default configuration file: default.conf
and leave nginx.conf
as provided – which is already set to include the default configurations.
# Remove the default configuration file
rm -v /etc/nginx/sites-available/default
# Create a new, blank configuration
nano /etc/nginx/conf.d/default.conf
Replace the files contents with the ones from below, again amending the necessary bits to suit your needs:
upstream app {
# Path to Unicorn SOCK file, as defined previously
server unix:/tmp/unicorn.myapp.sock fail_timeout=0;
}
server {
listen 80;
# Set the server name, similar to Apache's settings
server_name localhost;
# Application root, as defined previously
root /var/www/my_app/public;
try_files $uri/index.html $uri @app;
location @app {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://app;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}
Save and exit by pressing CTRL+X and confirming with Y.
Note: To learn more about Nginx, you can refer to How to Configure Nginx Web Server on a VPS.
Let’s start the Unicorn and run it as a daemon using the configuration file:
# Make sure that you are inside the application directory
# i.e. /my_app
unicorn -c unicorn.rb -D
Next, we are ready to reload and restart Nginx:
service nginx restart
And that’s it! You can now check out your deployment by going to your droplet’s IP address (or the domain name associated to it).
http://162.243.74.190/
# Hello world!
Setting up a firewall using IP Tables
How To Protect SSH with fail2ban on Ubuntu How To Protect SSH with fail2ban on CentOS 6
How To Send E-Mail Alerts on a CentOS VPS for System Monitoring
How To Install and Use Logwatch Log Analyser and Reporter
How to Optimize Unicorn Workers in a Ruby on Rails App
<div class=“author”>Submitted by: <a href=“https://twitter.com/ostezer”>O.S. Tezer</a></div>
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
I’m having this issue: /root/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/unicorn-5.0.0/lib/unicorn/configurator.rb:88:in `block in reload’: directory for pid=/var/www/MYAPPDIRECTORY(CENSORED)/pids/unicorn.pid not writable (ArgumentError) followed by many more lines and finally: master failed to start, check stderr log for details
Is there something wrong that I’m doing? I followed the instructions exactly the way it is but I can’t start unicorn with the unicorn -c unicorn.rb -D command. I think it could be a permissions issue, but I’m not sure.
this could probably be two different blog posts. It’d greatly increase readability!
I have manage to mount the server, if I go to
http://IP_ADDRESS
everything’s fine.But if I add more “server_name” to my domain and try to access it, I get a 502 Bad Gateway error. How can I fix it?
I’ve followed the tutorial to the line, but I end up getting the Welcome to nginx page.
I’m assuming this is an nginx problem, because the Unicorn logs show nothing weird, could this be related to the permissions of my
deploy
user? I did it throughadduser deploy
and manually adding it tovisudo
.Thanks for this thorough tutorial. It may also be nice to have a reminder to update apt-get in the very beginning:
apt-get update
. My particular box didn’t correctly resolve some of the referenced packages until I did that.@hackpravj: Glad to hear it’s working now! Yes, Andrew and I are DigitalOcean employees :) We’re working on some pretty awesome features for the community including notifications which should be released soon, stay tuned!
Cool, thank you both Kamal Nasser and Andrew SB things are working now :)
btw one silly question : are you a digitalocean employee, as I can’t see any notification when you reply to my comment but you answered very soon, so I’m guessing you are an employee or I’m extra dumb ?
@hackpravj: Odd, you can just add the variable yourself (below
. /lib/lsb/init-functions
):should I write it by myself ? :P it that fine, if I do so ?
hey, is this happening because of empty
/etc/default/nginx
itself ? as inside/etc/init.d/nginx
it is sourcing another file/etc/default/nginx
(empty, in my case).and checks for variable
$NGINX_ENABLED
. which is undefined here, I think.