NavigationContentFooter
Jump toSuggest an edit

Deploying a Ruby on Rails application on Scaleway

Reviewed on 16 July 2024Published on 20 January 2020
  • Ruby-on-Rails
  • nginx
  • rbenv
  • puma
  • PostgreSQL
  • systemd
  • vps

Ruby on Rails is a server-side web application framework written in Ruby and released under the MIT License.

The popularity of Ruby on Rails emerged in the year 2000 and was greatly influenced by web application development. The framework encourages and facilitates the use of common web standards such as JSON, HTML, CSS and JavaScript for user interfacing.

This article describes basic steps to set up a deployment-ready VPS Instance using Capistrano with rbenv, Puma as the application server, and Nginx as the reverse proxy. Let’s assume we already have a working Rails application, hosted on a remote git repository.

In this tutorial, you will learn basic steps to configure a deployment-ready machine using Capistrano, rbenv, Puma as application server, and Nginx as a reverse proxy. We assume that you have already a working Ruby on Rails application and deploy it using the following stack on an Instance:

  • nginx as the reverse proxy
  • PostgreSQL as the database
  • puma for the Ruby concurrent web server
  • rbenv for managing several Ruby versions
  • Capistrano as deployment utility
  • Ubuntu 22.04 as operating system and Systemd for keeping processes up.

Before you start

To complete the actions presented below, you must have:

  • A Scaleway account logged into the console
  • Owner status or IAM permissions allowing you to perform actions in the intended Organization
  • An SSH key
  • An Instance or Elastic Metal server running on Ubuntu Jammy Jellyfish (22.04 LTS)
  • sudo privileges or access to the root user

Installing rbenv and Node.js

Ruby on Rails needs to have a Ruby runtime to run. It could be tempting to just install the Ruby version provided by the distribution.

The problem with that is that this version might be different from the one we currently need.

Because we want to have the liberty to choose the version of Ruby we want, we will install a dedicated Ruby version manager called rbenv.

This version will also come with a modern Ruby installation, but you are free to change it to fit your needs.

  1. The installation is done using the apt package manager of Ubuntu. Update the apt package cache before the installation to ensure you have the latest version available in the Ubuntu repository:
    root@instance:~# apt update
    root@instance:~# apt install -y rbenv
  2. Verify that the installation of rbenv and ruby was successful:
    root@instance:~# rbenv --version
    rbenv 1.2.0
    root@instance:~# ruby --version
    ruby 3.1.4p376 (2023-03-30 revision 0db68f0233) [x86_64-linux-gnu]

In this tutorial, we will use the default version of Ruby, but you are free to download yours to fit your needs. If you need a more recent version of rbenv, instructions can be found on the official installer page.

Installing the Node.js runtime

Ruby on Rails requires having nodejs installed to compile certain web assets. If you do not need a particular version of nodejs for your deployment you can use the default one provided by the operating system.

  1. Install nodejs using the apt package manager:
    root@instance:~# apt install nodejs
  2. To check if the installation has been successful, run the node --version command. The output should look similar to this:
    root@instance:~# node --version
    v18.16.0

Creating a non-admin user

Create a non-administrative user that will be able to run programs without having administrative privileges. We will call this user deploy.

  1. Create the user by running the following command as root:
    root@instance:~# adduser deploy
  2. To check that the user creation was successful, log into the user account using the su command and check the installed ruby version:
    root@instance:~# su - deploy
    deploy@instance:~$ ruby --version
    ruby 3.1.4p376 (2023-03-30 revision 0db68f0233) [x86_64-linux-gnu]

Installing and configuring PostgreSQL

Ruby on Rails typically uses an SQL database for data persistence. In this section, we will install all the dependencies required to have a database running.

  1. Install the default PostgreSQL provided by the operating system using the apt package manager.

    root@instance:~# apt install postgresql postgresql-contrib libpq-dev
  2. Check if the installation was successful by checking the status of the postgresql service:

    root@instance:~# service postgresql status
    ● postgresql.service - PostgreSQL RDBMS
    Loaded: loaded (/lib/systemd/system/postgresql.service; enabled; vendor preset: enabled)
    Active: active (exited) since Tue 2024-07-16 10:23:54 UTC; 12s ago
    Process: 5602 ExecStart=/bin/true (code=exited, status=0/SUCCESS)
    Main PID: 5602 (code=exited, status=0/SUCCESS)
    CPU: 1ms
    Jul 16 10:23:54 scw-gallant-neumann systemd[1]: Starting PostgreSQL RDBMS...
    Jul 16 10:23:54 scw-gallant-neumann systemd[1]: Finished PostgreSQL RDBMS.
  3. Create an unprivileged role and database so that Rails can connect to the database.

    Call this role deploy, just like our username on the instance:

  4. Use su to enter commands as the postgres user, which is the default administrator of PostgreSQL:

    root@instance:~# su - postgres
  5. Once logged as the Postgres user, create a database for the user deploy.

    Enter a strong password when prompted. It will be your database password.

    postgres@rails:~$ createuser --pwprompt deploy
    Enter password for new role:
    Enter it again:
    postgres@instance:~$ createdb -O deploy my_rails_app
    postgres@instance:~$ exit
  6. Once this is done, you can check that it worked by using the following command:

    root@instance:~# psql --user deploy -h localhost -d my_rails_app
    Password for user deploy:
    psql (14.10 (Ubuntu 14.10-0ubuntu0.22.04.1))
    SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
    Type "help" for help.
    my_rails_app=>

Above is the shell of the database, ready to receive queries. Exit it by typing \q.

Installing nginx and puma

Now we need to add a reverse proxy and a web server to our deployment. There are two parts: The first part

is nginx, a reverse proxy that will handle the incoming traffic. The second part is puma, a fast concurrent web server for Ruby and Rack.

Installing and configuring puma

Puma is the concurrent web server that will host your Ruby on Rails application. To install it, first, add the puma gem to your Rails application.

  1. Add puma to your Gemfile:
    gem "puma"
  2. Install the gem:
    deploy@instance:~$ bundle install
  3. To check that the installation was successful, run the following command:
    deploy@instance:~$ puma -V
    puma version 6.0.1

Installing and configuring nginx

Install the latest version of nginx provided by the operating system:

  1. Update the list of available packages:
    root@instance:~# apt update
  2. Install the nginx package:
    root@instance:~# apt install nginx
  3. Check the status of the nginx service:
    root@instance:~# service nginx status
    ● nginx.service - A high performance web server and a reverse proxy server
    Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
    Active: active (running) since Tue 2024-07-16 10:23:54 UTC; 1min 23s ago
    Main PID: 6002 (nginx)
    Tasks: 2 (limit: 2359)
    Memory: 2.6M
    CGroup: /system.slice/nginx.service
    ├─6002 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
    └─6003 nginx: worker process
    Jul 16 10:23:54 scw-gallant-neumann systemd[1]: Starting A high performance web server and a reverse proxy server...
    Jul 16 10:23:54 scw-gallant-neumann systemd[1]: nginx.service: Failed to parse PID from file /run/nginx.pid: Invalid argument
    Jul 16 10:23:54 scw-gallant-neumann systemd[1]: Started A high performance web server and a reverse proxy server.

If everything looks good, let’s move on to the next section.

Configuring Capistrano for deployment

Capistrano is a deployment utility we will use to automate the process of publishing our Rails application. It will perform tasks such as migrating the database, installing dependencies, and managing restart services.

  1. Add the capistrano gem to your Gemfile:
    group :development do
    gem 'capistrano', '~> 3.16'
    gem 'capistrano-rbenv'
    gem 'capistrano-bundler'
    gem 'capistrano-rails'
    gem 'capistrano3-puma'
    end
  2. Install the gem:
    deploy@instance:~$ bundle install
  3. Set up Capistrano:
    deploy@instance:~$ bundle exec cap install
  4. Edit the Capfile to use the required plugins:
    # Load DSL and Setup Up Stages
    require 'capistrano/setup'
    # Include default deployment tasks
    require 'capistrano/deploy'
    # Include tasks from other gems included in your Gemfile
    #
    # For documentation on these, see for example:
    #
    # https://github.com/capistrano/rbenv
    # https://github.com/capistrano/bundler
    # https://github.com/capistrano/rails
    # https://github.com/capistrano/passenger
    require 'capistrano/rbenv'
    require 'capistrano/bundler'
    require 'capistrano/rails/assets'
    require 'capistrano/rails/migrations'
    require 'capistrano/puma'
    install_plugin Capistrano::Puma
    install_plugin Capistrano::Puma::Nginx
    install_plugin Capistrano::Puma::Systemd
    # Load custom tasks from `lib/capistrano/tasks` if you have any defined
    Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
  5. Edit config/deploy.rb to configure the deployment:
    # config valid for current version and patch releases of Capistrano
    lock "~> 3.16.0"
    set :application, "my_rails_app"
    set :repo_url, "git@example.com:me/my_repo.git"
    # Default branch is :master
    # ask :branch, `git rev-parse --abbrev-ref HEAD`.chomp
    # Default deploy_to directory is /var/www/my_app_name
    set :deploy_to, "/var/www/my_rails_app"
    # Default value for :format is :airbrussh.
    # set :format, :airbrussh
    # You can configure the Airbrussh format using :format_options.
    # These are the defaults.
    # set :format_options, command_output: true, log_file: "log/capistrano.log", color: :auto, truncate: :auto
    # Default value for :pty is false
    # set :pty, true
    # Default value for :linked_files is []
    # append :linked_files, "config/database.yml"
    # Default value for linked_dirs is []
    # append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "public/system"
    # Default value for default_env is {}
    # set :default_env, { path: "/opt/ruby/bin:$PATH" }
    # Default value for local_user is ENV['USER']
    # set :local_user, -> { `git config user.name`.chomp }
    # Default value for keep_releases is 5
    # set :keep_releases, 5
    set :rbenv_type, :user # or :system, depends on your rbenv setup
    set :rbenv_ruby, '3.1.4'
    # set :rbenv_map_bins, %w{rake gem bundle ruby rails}
    set :rbenv_roles, :all # default value
    # Puma configurations
    set :puma_threads, [4, 16]
    set :puma_workers, 0
    # Don't change these unless you know what you're doing
    set :puma_bind, "unix://#{shared_path}/tmp/sockets/#{fetch(:application)}-puma.sock"
    set :puma_state, "#{shared_path}/tmp/pids/puma.state"
    set :puma_pid, "#{shared_path}/tmp/pids/puma.pid"
    set :puma_access_log, "#{release_path}/log/puma.error.log"
    set :puma_error_log, "#{release_path}/log/puma.access.log"
    set :puma_preload_app, true
    set :puma_worker_timeout, nil
    set :puma_init_active_record, true # Change to false when not using ActiveRecord
  6. Configure the stages config/deploy/production.rb:
    server 'your.server.ip', user: 'deploy', roles: %w{app db web}
    set :ssh_options, {
    forward_agent: true,
    auth_methods: %w(publickey),
    keys: %w(~/.ssh/id_rsa)
    }
  7. Create the necessary directories on the server:
    deploy@instance:~$ mkdir -p /var/www/my_rails_app/shared/config

Setting up Systemd services

Systemd will keep our Puma application server running, restart it on crashes, and handle the logging.

  1. Create a systemd service file for Puma:
    root@instance:~# nano /etc/systemd/system/puma.service
  2. Add the following content to the file:
    [Unit]
    Description=Puma HTTP Server
    After=network.target
    [Service]
    Type=simple
    User=deploy
    WorkingDirectory=/var/www/my_rails_app/current
    ExecStart=/home/deploy/.rbenv/shims/bundle exec puma -C /var/www/my_rails_app/current/config/puma.rb
    ExecReload=/bin/kill -TSTP $MAINPID
    Restart=always
    [Install]
    WantedBy=multi-user.target
  3. Reload systemd to apply the changes:
    root@instance:~# systemctl daemon-reload
  4. Enable the Puma service to start on boot:
    root@instance:~# systemctl enable puma

Setting up nginx as a reverse proxy

Configure nginx to forward traffic to Puma.

  1. Create a new nginx site configuration:
    root@instance:~# nano /etc/nginx/sites-available/my_rails_app
  2. Add the following content to the file:
    server {
    listen 80;
    server_name your.server.ip;
    root /var/www/my_rails_app/current/public;
    location / {
    proxy_pass http://unix:/var/www/my_rails_app/shared/tmp/sockets/my_rails_app-puma.sock;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    }
    location ~ ^/(assets|packs) {
    expires max;
    gzip_static on;
    }
    error_page 500 502 503 504 /500.html;
    client_max_body_size 2M;
    keepalive_timeout 10;
    }
  3. Enable the site by creating a symlink:
    root@instance:~# ln -s /etc/nginx/sites-available/my_rails_app /etc/nginx/sites-enabled/
  4. Test the nginx configuration:
    root@instance:~# nginx -t
  5. Restart nginx to apply the changes:
    root@instance:~# systemctl restart nginx

Deploying the application

Deploy the application using Capistrano.

  1. Deploy the application for the first time:
    deploy@instance:~$ bundle exec cap production deploy

This command will check out the code from the repository, install the necessary gems, precompile assets, and restart the application server.

Your Rails application should now be running on your server, accessible via the IP address or domain name configured in the nginx setup.

Conclusion

With the above steps, you’ve successfully set up a deployment workflow for a Rails application using a Capistrano, Puma, and Nginx stack on a fresh Ubuntu server. This setup ensures a smooth deployment process and a robust, scalable server environment.

API DocsScaleway consoleDedibox consoleScaleway LearningScaleway.comPricingBlogCareers
© 2023-2024 – Scaleway