Some time ago I deployed a new Rails 6 application to a subdirectory within a micro service architecture. Rails documentation provides some information on this but doesn't go into great details what each setting is affecting to. This post will fill in some gaps in the documentation and show one way to deploy Rails application to a subdirectory (also known as relative path). The exact version of Rails here was 6.0.3.4.

First they say you should set the following configuration (or alternatively the RAILS_RELATIVE_URL_ROOT environment variable):

config.relative_url_root = "/app1"

(Update: I noticed that this configuration does not work if it is set in an initializer file. It only had the desired effect when set in application.rb).

According to the docs

Rails will now prepend "/app1" when generating links.

Well that is true for the asset links but doesn't actually affect the path helpers (e.g. root_path). Those will still return paths directly under the document root / without the subdirectory and break the page. So how to fix that? Here the Rails docs implicitly assume the reader is somewhat familiar with web server interfaces, in this case Rack as Rails is a Rack application. The key web server variable here is the SCRIPT_NAME which can be found from the Rack spec. It is often set by the web server or reverse proxies like nginx.
If you're wondering the name of this variable, it comes from the good old times when web pages were actually served with dedicated scripts.

So how to set SCRIPT_NAME? Rails docs shows some example configurations for different servers but nowhere explicitly states that the critical part is the SCRIPT_NAME. Depending on your setup, you need to ensure that this variable is present when run Rails.application is called in your config.ru file.

In our case, however, we didn't want to chage our server setup but instead have the SCRIPT_NAME set up within the Rails application itself. It turned out that the easiest way to achieve that was to wrap the Rails application itself into a very short Rack application. This can be achieved with the map method of Rack as suggested here:

# configu.ru
map '/app1' do
  run Rails.application
end

I suspected this would alter the SCRIPT_NAME variable and indeed, by looking at the source code of Rack I found the following lines:

# In urlmap.rb#call

path        = env[PATH_INFO]
script_name = env[SCRIPT_NAME]

# ...

env[SCRIPT_NAME] = (script_name + location)
env[PATH_INFO] = rest

So we can see that the SCRIPT_NAME is appended with the location variable which holds the map argument value ('/app1' in the example).

After this your Rails app will have both the assets paths and path helpers configured for serving your Rails application from a subdirectory.