Deploying a Ruby app on an AWS production server
with Passenger in Standalone mode
On this page you will learn how you can deploy your app to a server together with Passenger. You can either follow these instructions with your own app, or you can use the sample Rails app we prepared.
Please tell us a bit about your app and your system
With this information, we can provide you with the most relevant instructions.
Table of contents
- Loading...
1 Update your gem bundle
We need to ensure that "passenger" is in your Gemfile.
Your Rails app needs a database in production. For demonstration purposes, this tutorial uses sqlite as database because it's the easiest to setup. So we need to ensure that "sqlite3" is in the Gemfile.
On your local computer, open your app's Gemfile. Remove lines that look like one of these:
gem "unicorn"
gem "thin"
gem "puma"
Make sure the following lines exists:
gem "passenger"
gem "sqlite3"
Note that the sqlite3
gem may be located inside a development
group, like this:
group :development do
gem "sqlite3"
end
# -OR-
gem "sqlite3", groups: [:development]
Since we are going to use sqlite3 in production in this tutorial, we need to ensure that this gem is moved outside the development
group, like this:
gem "sqlite3"
When you are done, install your gem bundle and commit the changes:
$ bundle install $ git commit -a -m "Use Passenger"
2 Transferring the app code to the server
2.1 Push your code to a Git repository
We want to transfer our application's code to the server. The easiest way to do that is via Git.
If you have already setup a Git repository, push your application's code to that repository by running this on your local computer:
$ git push
If you have not already setup a Git repository, go to Github, create a repository and push your application's code there.
2.2 Login to your server, create a user for the app
Login to your server with SSH:
$ ssh -i your_ec2_key.pem adminuser@yourserver.com
Replace adminuser
with the name of an account with administrator privileges or sudo privileges.
This is usually admin
, ec2-user
, root
or ubuntu
.
Now that you have logged in, you should create an operating system user account for your app. For security reasons, it is a good idea to run each app under its own user account, in order to limit the damage that security vulnerabilities in the app can do. Passenger will automatically run your app under this user account as part of its user account sandboxing feature.
You should give the user account the same name as your app. But for demonstration purposes, this tutorial names the user account myappuser
.
$ sudo adduser myappuser
We also ensure that that user has your SSH key installed:
$ sudo mkdir -p ~myappuser/.ssh $ touch $HOME/.ssh/authorized_keys $ sudo sh -c "cat $HOME/.ssh/authorized_keys >> ~myappuser/.ssh/authorized_keys" $ sudo chown -R myappuser: ~myappuser/.ssh $ sudo chmod 700 ~myappuser/.ssh $ sudo sh -c "chmod 600 ~myappuser/.ssh/*"
2.3 Install Git on the server
Debian, Ubuntu | sudo apt-get install -y git |
Red Hat, CentOS, Fedora, Scientific Linux, Amazon Linux | sudo yum install -y git |
Other operating systems | Please install Git from git-scm.com. |
2.4 Pull code
You need to pick a location in which to permanently store your application's code. A good location is /var/www/APP_NAME
. Let us create that directory.
$ sudo mkdir -p /var/www/myapp $ sudo chown myappuser: /var/www/myapp
Replace myapp
and myappuser
with your app's name and your app user account's name.
Now let us pull the code from Git:
$ cd /var/www/myapp $ sudo -u myappuser -H git clone git://github.com/username/myapp.git code
If you are using our sample app, use this Git clone command instead:
$ cd /var/www/myapp $ sudo -u myappuser -H git clone --branch=end_result https://github.com/phusion/passenger-ruby-rails-demo.git code
Your app's code now lives on the server at /var/www/myapp/code
.
3 Preparing the app's environment
3.1 Login as the app's user
All subsequent instructions must be run under the application's user account. While logged into your server, login under the application's user account as follows:
$ sudo -u myappuser -H bash -l
Since you are using RVM, make sure that you activate the Ruby version that you want to run your app under. For example:
$ rvm use ruby-2.3.3
3.2 Install app dependencies
Your application has various dependencies.
They must be installed. Most of these dependencies are gems in your Gemfile, managed by Bundler. You can install them by running bundle install --deployment --without development test -j 2
in your app's directory:
$ cd /var/www/myapp/code $ bundle install --deployment --without development test
Your app may also depend on services, such as PostgreSQL, Redis, etc. Installing services that your app depends on is outside of this tutorial's scope.
3.3 Configure database.yml and secrets.yml
Since your Rails app probably needs a database, you need to edit config/database.yml. For demonstration purposes, we will setup your app with an SQLite database because that is the easiest.
Open the file:
$ nano config/database.yml
Ensure that the production
section looks like this:
production:
adapter: sqlite3
database: db/production.sqlite3
Rails also needs a unique secret key with which to encrypt its sessions. Starting from Rails 4, this secret key is stored in config/secrets.yml. But first, we need to generate a secret key. Run:
$ bundle exec rake secret ...
This command will output a secret key. Copy that value to your clipboard. Next, open config/secrets.yml:
$ nano config/secrets.yml
If the file already exists, look for this:
production:
secret_key_base: <%=ENV["SECRET_KEY_BASE"]%>
Then replace it with the following. If the file didn't already exist, simply insert the following.
production: secret_key_base: the value that you copied from 'rake secret'
To prevent other users on the system from reading sensitive information belonging to your app, let's tighten the security on the configuration directory and the database directory:
$ chmod 700 config db $ chmod 600 config/database.yml config/secrets.yml
3.4 Compile Rails assets and run database migrations
Run the following command to compile assets for the Rails asset pipeline, and to run database migrations:
$ bundle exec rake assets:precompile db:migrate RAILS_ENV=production
4 Starting the app in Passenger
Now that you are done with transferring your app's code to the server and setting up an environment for your app, it is time to start your app in Passenger.
4.1 Create a Passenger config file
Since this is a production environment, we need to customize Passenger a little bit. Go to your application's
code directory
and create a file named Passengerfile.json
:
$ cd /var/www/myapp/code $ nano Passengerfile.json
Insert:
{ // Run the app in a production environment. The default value is "development". "environment": "production", // Run Passenger on port 80, the standard HTTP port. "port": 80, // Tell Passenger to daemonize into the background. "daemonize": true, // Tell Passenger to run the app as the given user. Only has effect // if Passenger was started with root privileges. "user": "myappuser" }
Replace myappuser
with your app's user account name.
4.2 Go back to the admin account
You have previously logged into your app's user account in order to prepare the app's environment. That user does not have sudo access. In the next steps, you need to start Passenger with root privileges, for which sudo access is needed. So you need to switch back to the admin account.
This can be done by simply exiting the shell that was logged into the app's user account. You will then be dropped back to the admin account. For example:
# This is what you previously ran: admin$ sudo -u myappuser -H bash -l myappuser$ ... # Type `exit` to go back to the account you were before myappuser$ exit admin$ _
4.3 Start Passenger Standalone
While in your application's code directory, start Passenger. As configured, it will start on port 80 and will daemonize into the background.
Note that, because Passenger is configured to listen on port 80, this command must be run with root privileges. Only root privileged processes can listen on ports lower than 1024.
$ cd /var/www/myapp/code $ rvmsudo bundle exec passenger start
$ cd /var/www/myapp/code $ sudo bundle exec passenger start
4.4 Test drive
You should now be able to access your app through the server's host name! Try running this from your local computer. Replace yourserver.com
with your server's hostname.
$ curl http://yourserver.com/ ...your app's front page HTML...
If you do not see your app's front page HTML, then the most likely cause is that you did not setup DNS records. Setting up DNS is outside the scope of this tutorial. In the mean time, we recommend that you use your server's IP address as the server name.
4.5 Make sure Passenger Standalone starts on system boot
Passenger is now running and serving your app, but that only lasts until you reboot your server. So you must configure your server to start Passenger Standalone on system boot.
The easiest way to do that is to add it to the file /etc/rc.local
. This script is called during system boot. Ensure that it's executable and open it in a text editor:
$ sudo chmod +x /etc/rc.local $ sudo nano /etc/rc.local
Here is an example of what you may want to add to /etc/rc.local
. If there is an exit
command in rc.local, make sure you add these before the exit command.
#!/bin/sh # Change working directory to your webapp. cd /var/www/myapp/code # Start Passenger Standalone in daemonized mode. Passenger will be started as # root when run from this file, but Passengerfile.json tells it to drop its # privileges to a normal user. # Replace '2.3.3' with the Ruby version you want your app to run under. /usr/local/rvm/bin/rvm-exec ruby-2.3.3 \ bundle exec passenger startbundle exec passenger start
1 Add "passenger" to your gem bundle
We need to ensure that "passenger" is in your Gemfile.
On your local computer, open your app's Gemfile. Remove lines that look like one of these:
gem "unicorn"
gem "thin"
gem "puma"
Make sure the following line exists:
gem "passenger", ">= 5.0.11"
When you are done, install your gem bundle and commit the changes:
$ bundle install $ git commit -a -m "Use Passenger"
2 Transferring the app code to the server
2.1 Push your code to a Git repository
We want to transfer our application's code to the server. The easiest way to do that is via Git.
If you have already setup a Git repository, push your application's code to that repository by running this on your local computer:
$ git push
If you have not already setup a Git repository, go to Github, create a repository and push your application's code there.
2.2 Login to your server, create a user for the app
Login to your server with SSH:
$ ssh -i your_ec2_key.pem adminuser@yourserver.com
Replace adminuser
with the name of an account with administrator privileges or sudo privileges.
This is usually admin
, ec2-user
, root
or ubuntu
.
Now that you have logged in, you should create an operating system user account for your app. For security reasons, it is a good idea to run each app under its own user account, in order to limit the damage that security vulnerabilities in the app can do. Passenger will automatically run your app under this user account as part of its user account sandboxing feature.
You should give the user account the same name as your app. But for demonstration purposes, this tutorial names the user account myappuser
.
$ sudo adduser myappuser
We also ensure that that user has your SSH key installed:
$ sudo mkdir -p ~myappuser/.ssh $ touch $HOME/.ssh/authorized_keys $ sudo sh -c "cat $HOME/.ssh/authorized_keys >> ~myappuser/.ssh/authorized_keys" $ sudo chown -R myappuser: ~myappuser/.ssh $ sudo chmod 700 ~myappuser/.ssh $ sudo sh -c "chmod 600 ~myappuser/.ssh/*"
2.3 Install Git on the server
Debian, Ubuntu | sudo apt-get install -y git |
Red Hat, CentOS, Fedora, Scientific Linux, Amazon Linux | sudo yum install -y git |
Other operating systems | Please install Git from git-scm.com. |
2.4 Pull code
You need to pick a location in which to permanently store your application's code. A good location is /var/www/APP_NAME
. Let us create that directory.
$ sudo mkdir -p /var/www/myapp $ sudo chown myappuser: /var/www/myapp
Replace myapp
and myappuser
with your app's name and your app user account's name.
Now let us pull the code from Git:
$ cd /var/www/myapp $ sudo -u myappuser -H git clone git://github.com/username/myapp.git code
If you are using our sample app, use this Git clone command instead:
$ cd /var/www/myapp $ sudo -u myappuser -H git clone --branch=end_result https://github.com/phusion/passenger-ruby-rails-demo.git code
Your app's code now lives on the server at /var/www/myapp/code
.
3 Preparing the app's environment
3.1 Login as the app's user
All subsequent instructions must be run under the application's user account. While logged into your server, login under the application's user account as follows:
$ sudo -u myappuser -H bash -l
Since you are using RVM, make sure that you activate the Ruby version that you want to run your app under. For example:
$ rvm use ruby-2.3.3
3.2 Install app dependencies
Your application has various dependencies.
They must be installed. Most of these dependencies are gems in your Gemfile, managed by Bundler. You can install them by running bundle install --deployment --without development test -j 2
in your app's directory:
$ cd /var/www/myapp/code $ bundle install --deployment --without development test
Your app may also depend on services, such as PostgreSQL, Redis, etc. Installing services that your app depends on is outside of this tutorial's scope.
4 Starting the app in Passenger
Now that you are done with transferring your app's code to the server and setting up an environment for your app, it is time to start your app in Passenger.
4.1 Create a Passenger config file
Since this is a production environment, we need to customize Passenger a little bit. Go to your application's
code directory
and create a file named Passengerfile.json
:
$ cd /var/www/myapp/code $ nano Passengerfile.json
Insert:
{ // Run the app in a production environment. The default value is "development". "environment": "production", // Run Passenger on port 80, the standard HTTP port. "port": 80, // Tell Passenger to daemonize into the background. "daemonize": true, // Tell Passenger to run the app as the given user. Only has effect // if Passenger was started with root privileges. "user": "myappuser" }
Replace myappuser
with your app's user account name.
4.2 Go back to the admin account
You have previously logged into your app's user account in order to prepare the app's environment. That user does not have sudo access. In the next steps, you need to start Passenger with root privileges, for which sudo access is needed. So you need to switch back to the admin account.
This can be done by simply exiting the shell that was logged into the app's user account. You will then be dropped back to the admin account. For example:
# This is what you previously ran: admin$ sudo -u myappuser -H bash -l myappuser$ ... # Type `exit` to go back to the account you were before myappuser$ exit admin$ _
4.3 Start Passenger Standalone
While in your application's code directory, start Passenger. As configured, it will start on port 80 and will daemonize into the background.
Note that, because Passenger is configured to listen on port 80, this command must be run with root privileges. Only root privileged processes can listen on ports lower than 1024.
$ cd /var/www/myapp/code $ rvmsudo bundle exec passenger start
$ cd /var/www/myapp/code $ sudo bundle exec passenger start
4.4 Test drive
You should now be able to access your app through the server's host name! Try running this from your local computer. Replace yourserver.com
with your server's hostname.
$ curl http://yourserver.com/ ...your app's front page HTML...
If you do not see your app's front page HTML, then the most likely cause is that you did not setup DNS records. Setting up DNS is outside the scope of this tutorial. In the mean time, we recommend that you use your server's IP address as the server name.
4.5 Make sure Passenger Standalone starts on system boot
Passenger is now running and serving your app, but that only lasts until you reboot your server. So you must configure your server to start Passenger Standalone on system boot.
The easiest way to do that is to add it to the file /etc/rc.local
. This script is called during system boot. Ensure that it's executable and open it in a text editor:
$ sudo chmod +x /etc/rc.local $ sudo nano /etc/rc.local
Here is an example of what you may want to add to /etc/rc.local
. If there is an exit
command in rc.local, make sure you add these before the exit command.
#!/bin/sh # Change working directory to your webapp. cd /var/www/myapp/code # Start Passenger Standalone in daemonized mode. Passenger will be started as # root when run from this file, but Passengerfile.json tells it to drop its # privileges to a normal user. # Replace '2.3.3' with the Ruby version you want your app to run under. /usr/local/rvm/bin/rvm-exec ruby-2.3.3 \ bundle exec passenger startbundle exec passenger start
Next step
Congratulations, you have successfully deployed your app!
Continue: Deploying updates »