Running old Ruby on Rails application with Docker | Karol Działowski

Running old Ruby on Rails application with Docker

A few years ago, in 2016 I was developing Ruby on Rails applications. It was the beginning of my programming journey so I was not paying attention to containerization or even proper documentation. This made running those apps in 2022 a little bit harder.

As an example, I'll show the process based on my euro2016 repository. Looking at the Gemfile you can see that there are not many dependencies there - the process should be easier than a real commercial big application. The website was created using rails 4.1.8 which was released in 2014! Almost 10 years ago.

First try: running on host

Firstly I thought maybe it's possible to install and run the app directly on my computer (mac). I installed ruby and ran bundle install. Sadly it was not so easy. Some old dependencies were causing problems.

euro2016 on  master [?] via 💎 v2.6.8
✗  bundle install

An error occurred while installing json (1.8.3), and Bundler cannot
continue.
Make sure that gem install json -v '1.8.3' --source 'https://rubygems.org/'
succeeds before bundling.

In Gemfile:
  rails was resolved to 4.1.8, which depends on
    actionmailer was resolved to 4.1.8, which depends on
      actionpack was resolved to 4.1.8, which depends on
        actionview was resolved to 4.1.8, which depends on
          activesupport was resolved to 4.1.8, which depends on
            json

Having this error I tried to install the newer version of json gem but new problems arrived. Foremost I wanted to run the app in the same state as it was before. Upgrading dependencies might change some behavior and I didn't want that.

After some googling, a few hours spend tinkering with the build process, and even changing versions there was no progress. As a next step I tried using rbenv. I thought installing an older version of ruby may solve some problems but installing the older version caused even more problems in the first place. Some dependency was not working because some os-package was updated (like libssl or something simillar). I didn't want to install old packages on my OS and here comes the Docker.

Second try: containerization with Docker

Using Docker for Ruby on Rails is pretty straightforward. You use ruby image and that's it. As a first iteration I tried the basic Dockerfile:

FROM ruby
WORKDIR /root

ADD Gemfile /root
ADD Gemfile.lock /root

RUN bundle install --without production

We use ruby:latest image here with some Ruby 3 version. Copying Gemfile and Gemfile.lock and running bundle install. So what was the problem with this approach?

#9 39.00     @package.dir_mode = options[:dir_mode]
#9 39.00             ^^^^^^^^^^^
#9 39.04 An error occurred while installing rake (11.1.2), and Bundler cannot continue.
#9 39.04 Make sure that `gem install rake -v '11.1.2'` succeeds before bundling.
------
executor failed running [/bin/sh -c bundle install --without production]: exit code: 5

Then I thought maybe the latest version of ruby is not the best. I didn't know what version of ruby I was using in 2016. I should have documented it and made my life easier. As a wild guess I tried ruby:1.9.3 and got the following error:

#0 34.15 Installing actionpack 4.1.8
#0 34.57
#0 34.57 Gem::InstallError: mime-types-data requires Ruby version >= 2.0.
#0 34.58 An error occurred while installing mime-types-data (3.2016.0521), and Bundler
#0 34.58 cannot continue.
#0 34.58 Make sure that `gem install mime-types-data -v '3.2016.0521'` succeeds before
#0 34.58 bundling.
------

Ok, some package required Ruby version >= 2.0. After a few more tries I managed to find the proper version which is Ruby 2.1.

So all dependencies were installed correctly. To simplify the process I created docker-compose.yml which is used simply to build and start (serve) the application:

version: '3'
services:
  rails-app:
    build:
      context: .
      dockerfile: Dockerfile
    image: euro2016-rails
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    ports:
      - '3000:3000'
    working_dir: /root
    volumes:
      - .:/root:cached

I tried to serve the application but the new problem (or a challenge) arrived:

[+] Running 1/1
 - Container euro2016-rails-app-1  Recreated                                                                       0.1s
Attaching to euro2016-rails-app-1
euro2016-rails-app-1  | /usr/local/lib/ruby/gems/2.1.0/gems/bundler-1.15.1/lib/bundler/runtime.rb:85:in `rescue in block (2 levels) in require': There was an error while trying to load the gem 'uglifier'. (Bundler::GemRequireError)
euro2016-rails-app-1  | Gem Load Error is: Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes.
euro2016-rails-app-1  | Backtrace for gem load error is:
euro2016-rails-app-1  | /usr/local/bundle/gems/execjs-2.7.0/lib/execjs/runtimes.rb:58:in `autodetect'

Oh no, we don't have JavaScript runtime. That means we need to install nodejs. It will be easy, I have to add only apt-get install nodejs to the Dockerfile, right? Actually no.

 => ERROR [6/6] RUN apt-get install -y --force-yes nodejs                                                                               3.0s
------
 > [6/6] RUN apt-get install -y --force-yes nodejs:
#0 1.303 Reading package lists...
#0 2.955 Building dependency tree...
#0 2.957 Reading state information...
#0 2.962 E: Unable to locate package nodejs
------
failed to solve: executor failed running [/bin/sh -c apt-get install -y --force-yes nodejs]: exit code: 100

This ruby:2.1 image uses some old Debian 8 underhood and there is no nodejs in the debian repositories. After quick googling I found that on nodesource there is a script for adding nodejs to the repository. Final Dockerfile looks like this:

FROM ruby:2.1
WORKDIR /root

ADD Gemfile /root
ADD Gemfile.lock /root

RUN bundle install --without production

RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -

RUN apt-get install -y --force-yes nodejs
RUN node --version
RUN npm --version

Now I can run the application on any computer just having docker and running docker compose up. It works! The next step is populating the database, adding demo accounts, and hosting it on cloud as a showcase.

Screenshot of running application

Screenshot of running application

Key takeouts

For future projects I should:

  • create a docker image for building/serving application
  • document versions used
    • os (e.g. Ubuntu 20.04)
    • package managers, e.g. npm 2.1.3
    • runtime versions, e.g. node 16.3.8
© karlosos 2020 Open sourced on