Step by step guide to dockerize Rails application

24 Jun 2023 - Chandan J

  1. Create Dockerfile in Rails application’s root directory
    # cd to application root directory
    $> touch Dockerfile
    
  2. Edit the Dockerfile. You can remove the comments, as they’re just for your understanding purpose.
# Install required image of Ruby
# Every Dockerfile start with "FROM" base image 
FROM ruby:3.2.2

# Download latest package information
# -y : Yes to any prompt, -qq : Really queit mode
RUN apt-get update -yqq
# install nodejs and DO NOT install other recommended packages as we don't need them
# NOTE: nodejs is NOT required with Rails 7
RUN apt-get install -yqq --no-install-recommends nodejs

# Copy the all files in current folder (.) i.e. Application root to `/usr/src/app`
COPY . /usr/src/app

# docker run [OPTIONS] <our custom image> bin/rails server
# This command will fail, because by default container's working directory is `/`
# which doesn't have our rails application. Our rails application is IN `/usr/src/app`
# Thus change the working directory with WORKDIR
WORKDIR /usr/src/app

# Now we can run any command with `RUN`
RUN bundle install

3 Build the image

# docker build [options] path/to/build/directory
# In your application root directory, run docker build command
$> docker build .
[+] Building 32.4s (11/11) FINISHED                                                                                                                                         
 => [internal] load .dockerignore                                                                                                                                      0.0s
 => => transferring context: 2B                                                                                                                                        0.0s
 => [internal] load build definition from Dockerfile                                                                                                                   0.0s
 => => transferring dockerfile: 234B                                                                                                                                   0.0s
 => [internal] load metadata for docker.io/library/ruby:3.2.2                                                                                                          0.0s
 => [1/6] FROM docker.io/library/ruby:3.2.2                                                                                                                            0.0s
 => [internal] load build context                                                                                                                                      1.4s
 => => transferring context: 21.90MB                                                                                                                                   1.4s
 => [2/6] RUN apt-get update -yqq                                                                                                                                      3.5s
 => [3/6] RUN apt-get install -yqq --no-install-recommends nodejs                                                                                                      2.9s
 => [4/6] COPY . /usr/src/app                                                                                                                                          0.7s
 => [5/6] WORKDIR /usr/src/app                                                                                                                                         0.0s
 => [6/6] RUN bundle install                                                                                                                                          24.7s
 => exporting to image                                                                                                                                                 0.6s
 => => exporting layers                                                                                                                                                0.6s
 => => writing image sha256:e6ed3b8666fb59cwe9f9217549764af3cbd45d3b9b04e17f35eed04d0x7e14d21                                                                           0.0s

  1. Where is the image built?
# Notice the IMAGE ID (e6ed3b8666fb) and the last line of our `docker build .` image id. 
$➜ docker images
REPOSITORY                                 TAG                           IMAGE ID       CREATED          SIZE
<none>                                     <none>                        e6ed3b8666fb   31 minutes ago   1.01GB
ambassador/telepresence-docker-extension   1.0.9                         a3b57dd2c34f   11 days ago      418MB

  1. Run the image
$> docker run -p 3000:3000 e6ed3b8666fb \
  bin/rails s -b 0.0.0.0
=> Booting Puma
=> Rails 7.0.5 application starting in development 
=> Run `bin/rails server --help` for more startup options
Puma starting in single mode...
* Puma version: 5.6.6 (ruby 3.2.2-p53) ("Birdie's Version")
*  Min threads: 5
*  Max threads: 5
*  Environment: development
*          PID: 1
* Listening on http://0.0.0.0:3000
Use Ctrl-C to stop

Now go to localhost:3000 and you can see the Rails application’s homepage.

We need to start rails s with binding -b 0.0.0.0, because by default rails s - starts on localhost, i.e. 127.0.0.1. This ip can be access only from the machine, where the rails server is running. Our docker image is running on Linux distro, i.e. light-weight VM. So, our host machine can’t access this localhost. We’re binding to 0.0.0.0, which is equivalent to all IPv4 address on this machine.

The actual IP address of container can be found with following:

± ➜ docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                    NAMES
2b95d7d9faae   e6ed3b8666fb   "bin/rails s -b 0.0.…"   15 minutes ago   Up 15 minutes   0.0.0.0:3000->3000/tcp   nostalgic_chatterjee

± ➜ docker inspect --format \
> '' 2b95d7d9faae
172.17.0.2

Give a name to your docker image:

$➜ docker tag e6ed3b8666fb orendaa

$➜ docker images
REPOSITORY                                 TAG                           IMAGE ID       CREATED             SIZE
orendaa                                    latest                        e6ed3b8666fb   About an hour ago   1.01GB

$➜ docker tag e6ed3b8666fb orendaa:1.0.0

# Notice both latest and 1.0.0 has same IMAGE ID, as they're same image
$➜ docker images
REPOSITORY                                 TAG                           IMAGE ID       CREATED             SIZE
orendaa                                    1.0.0                         e6ed3b8666fb   About an hour ago   1.01GB
orendaa                                    latest                        e6ed3b8666fb   About an hour ago   1.01GB

You can provide tag while building the image:

# Single tag - latest
$> docker build -t orendaa .
# Multiple tags
$> docker build -t orendaa -t orendaa:1.0.0 .

Now you can run your specific image:

$> docker run -p 3000:3000 orendaa:1.0.0 \
       bin/rails s -b 0.0.0.0