How to Properly Deploy a Rails App

Deploying a production ready Ruby on Rails app on a Linux Server should be the easiest thing in the world. Nevertheless, information on this subject is scarse, outdated or stunningly confusing. Even more so when using a ruby version management system like rbenv or rvm who demand for a user to have a certain environment via the shells startup script.

Puma, the built-in Ruby/Rack web server in Rails 5 is solid and describes itself as "simple, fast, threaded, and highly concurrent". We can safely use it in a production environment and further simplify in dealing with this problem.

Systemd

On many Linux distributions the default system and service manager systemd offers you the option to run your production app as a service on a silver platter. There's really no need to keep a login shell open and manually run the app. Isn't it just better to simply run it as a service without having to manually interfere and worrying about its state? An apps self-recreation after a reboot or crash gives you peace of mind.

Create the service file

cd /etc/systemd/system
touch MyRailsApp.service
[Unit]
Description=MyRailsApp
After=network.target

[Service]
Type=simple
User={username}
WorkingDirectory={/path/to/app/directory}
ExecStart=/home/{username}/.rbenv/bin/rbenv exec bundle exec rails s -p 4567
Environment="RAILS_ENV=production"
Environment="SECRET_KEY_BASE={secret string}"
Environment="RBENV_ROOT=/home/{username}/.rbenv"
Environment="NODENV_ROOT=/home/{username}/.nodenv"
Environment="PATH=/home/{username}/.rbenv/bin:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin"
TimeoutSec=30
RestartSec=15s
Restart=always

[Install]
WantedBy=multi-user.target

Run the service

# Run these
systemctl daemon-reload
systemctl restart MyRailsApp
systemctl status MyRailsApp

# Get that
● MyRailsApp.service - MyRailsApp
   Loaded: loaded (/etc/systemd/system/MyRailsApp.service; enabled; vendor preset: disabled)                                      Active: active (running) since Mon 2017-11-13 21:25:27 CET; 10s ago
 Main PID: 28757 (ruby)
   CGroup: /system.slice/MyRailsApp.service
           └─28757 puma 3.10.0 (tcp://0.0.0.0:4567) [MyRailsApp]

Nov 13 21:25:27 phoenix systemd[1]: Starting MyRailsApp...
Nov 13 21:25:38 phoenix rbenv[28757]: => Booting Puma
Nov 13 21:25:38 phoenix rbenv[28757]: => Rails 5.1.4 application starting in production
Nov 13 21:25:38 phoenix rbenv[28757]: => Run `rails server -h` for more startup options
Nov 13 21:25:38 phoenix rbenv[28757]: Puma starting in single mode...
Nov 13 21:25:38 phoenix rbenv[28757]: * Version 3.10.0 (ruby 2.4.2-p198), codename: Russell\'s Teapot
Nov 13 21:25:38 phoenix rbenv[28757]: * Min threads: 5, max threads: 5
Nov 13 21:25:38 phoenix rbenv[28757]: * Environment: production
Nov 13 21:25:38 phoenix rbenv[28757]: * Listening on tcp://0.0.0.0:4567
Nov 13 21:25:38 phoenix rbenv[28757]: Use Ctrl-C to stop

Apache Reverse Proxy Configuration

<Macro RailsProd $servername $folder $port>
  <VirtualHost *:80>
    ServerName $servername
    Redirect permanent / https://$servername/
  </VirtualHost>
  <VirtualHost *:443>
    ServerName $servername
    DocumentRoot "$folder/public/"

    <Location />
      Require all granted
      Options FollowSymLinks
      AllowOverride None
      # MultiViews must be turned off.
      Order allow,deny
      Allow from all
    </Location>

    ProxyPass /favicon.ico !
    ProxyPass /robots.txt !
    ProxyPassMatch ^/(404|422|500).html$ !
    ProxyPass /assets/ !
    ProxyPass /fonts/ !
    ProxyPass /static/ !

    ProxyPass / http://127.0.0.1:$port/
    ProxyPassReverse / http://127.0.0.1:$port/
    ErrorLog "/etc/httpd/logs/$servername_error.log"
    CustomLog "/etc/httpd/logs/$servername_access.log" common
    Include /etc/httpd/conf/include_ssl.conf
    SSLCertificateFile /etc/letsencrypt/live/$servername/cert.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/$servername/privkey.pem
    SSLCertificateChainFile /etc/letsencrypt/live/$servername/chain.pem
    Header always set X-Xss-Protection "0"
  </VirtualHost>
</Macro>
###################################################################################
# local                 servername              folder                         port
Use RailsProd           myrailsapp.com          /path/to/myrailsapp            4567