How to Dockerize your Rails 6 App from Scratch

Norfa Bagas
7 min readApr 7, 2020

--

Hello, all developers across the world!

In this post, I’m trying to give a tutorial about “How to dockerize rails 6 application from scratch.” By the end of this tutorial, a new rails 6 app with PostgreSQL will be created without installing Ruby language & PostgreSQL to our system.

Please note this:

  • This post is split into eight (8) steps,
  • this tutorial is based on Linux/Ubuntu system,
  • On some systems, we need to add sudo when running docker or docker-compose related tasks.

So let’s go straight to the tutorial:

First, we need to install Docker into our system.

If you are using Ubuntu 18.04 LTS then you can follow this link:
https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-18-04

or use sudo snap install docker if you are using snap package manager.

  • skip this if you have installed Docker to your system.

Second, create an empty directory. For example, we want to make our project name docker-rails by typing
$ mkdir docker-rails && cd docker-rails

Third, let’s define our Dockerfile and docker-compose files

$ touch Dockerfile

# DockerfileFROM ruby:2.6.5# Add yarn from repositoryRUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list# Install required packageRUN apt-get update -qq \&& apt-get install -y \apt-utils \nodejs \yarn \nano# Create directory docker-railsRUN mkdir /docker-rails# Change directory to docker-railsWORKDIR /docker-rails# Copy created Gemfile & Gemfile.lock to docker containerCOPY Gemfile /docker-rails/GemfileCOPY Gemfile.lock /docker-rails/Gemfile.lock# Install dependenciesRUN bundle installCOPY . /docker-railsEXPOSE 3000 3035CMD ["rails", "server", "-b", "0.0.0.0"]

This Dockerfile file is self-explanatory. First, we define to use a specific Ruby version (in this Dockerfile, ruby version 2.6.5). And then install the required package. And we need to include yarn because in Rails 6 yarn is the default compiler.

Then we create the docker-rails directory and change to that directory. Copy our Gemfile and Gemfile.lock files (we will add these files later) and run bundle install to install all required gems to the docker image. And we copy our Rails files & folders (we will create these files & folders later) to our docker image, open port 3000 and 3035 (port 3000 for rails web server and port 3035 for compiling webpack assets)and run rails server to start built-in puma server.

$ touch docker-compose.yml

# docker-compose.ymlversion: '3'services:  rails-db:    image: postgres    container_name: rails-db    environment:      - POSTGRES_USER=postgres      - POSTGRES_PASSWORD=postgres      - POSTGRES_DB=postgres      - DATABASE_HOST=db-host    volumes:      - ./tmp/db:/var/lib/postgresql/data    networks:      - networks    ports:      - "5432:5432"  rails-web:    build: .      container_name: rails-web    volumes:      - .:/docker-rails    ports:      - "3000:3000"
- "3035:3035"
depends_on: - rails-db networks: - networksvolumes: rails-web: rails-db:networks: networks: driver: bridge

In this docker-compose file, we define 2 containers,

  • rails-db for our PostgreSQL database, and
  • rails-web for our Web Server

On rails-db, we need to set-up our environment, and it will be used on our rails-web container for connecting to the database.

Note: we can use a dotenv file (.env) and call it using ${ENV_KEY}.

On rails-web, we need to point our working files & folders to the created container folder (in this post: ./docker-rails/ directory).

Fourth, create Gemfile and Gemfile.lock, .ruby-version files

$ touch Gemfile

# Gemfilesource 'https://rubygems.org'ruby '2.6.5'gem 'rails', '~>6'

In this Gemfile file, we just need to define what ruby version we need to use and what rails version we want to install.

$ touch Gemfile.lock

# Gemfile.lock

Yes, this Gemfile.lock file is empty. We need this file because ruby gems need both of these files (Gemfile & Gemfile.lock).

And create .ruby-version file which will define our specific ruby version needed for this rails app.

$ touch .ruby-version

ruby-2.6.5

Additional information

When installing a fresh Rails 6 app, the installer script will automatically runrails webpacker:install. And sometimes this is a buggy task. Read more about this bug here:

This error exists because config/webpacker.yml file is missing on our fresh created Rails 6 app. And to fix this, create a new config/webpacker.yml file and copy this code.

$ touch config/webpacker.yml

# Note: You must restart bin/webpack-dev-server for changes to take effectdefault: &default  source_path: app/javascript  source_entry_path: packs  public_root_path: public  public_output_path: packs  cache_path: tmp/cache/webpacker  check_yarn_integrity: false  webpack_compile_output: true# Additional paths webpack should lookup modules# ['app/assets', 'engine/foo/app/assets']resolved_paths: []# Reload manifest.json on all requests so we reload latest compiled packscache_manifest: false# Extract and emit a css fileextract_css: falsestatic_assets_extensions:  - .jpg  - .jpeg  - .png  - .gif  - .tiff  - .ico  - .svg  - .eot  - .otf  - .ttf  - .woff  - .woff2extensions:  - .mjs  - .js  - .sass  - .scss  - .css  - .module.sass  - .module.scss  - .module.css  - .png  - .svg  - .gif  - .jpeg  - .jpgdevelopment:  <<: *default  compile: true# Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modulescheck_yarn_integrity: true# Reference: https://webpack.js.org/configuration/dev-server/dev_server:  https: false  host: 0.0.0.0  port: 3035  public: 0.0.0.0:3035  hmr: false  # Inline should be set to true if using HMR  inline: true  overlay: true  compress: true  disable_host_check: true  use_local_ip: false  quiet: false  pretty: false  headers:    'Access-Control-Allow-Origin': '*'  watch_options:    ignored: '**/node_modules/**'test:  <<: *default  compile: true# Compile test packs to a separate directorypublic_output_path: packs-testproduction:  <<: *default# Production depends on precompilation of packs prior to booting for performance.compile: false# Extract and emit a css fileextract_css: true# Cache manifest.json for performancecache_manifest: true

Fifth, install a fresh Rails 6 app on our directory

We need to install a Rails 6 app, and we will copy those files & folders to our rails-web container. Because we don’t install any Ruby language, we can do this by using our docker-compose file. Practically, to run task/script using our container, we can use:

$ docker-compose run [CONTAINER_NAME] script_to_be_run

And this is how we create a Rails 6 app using PostgreSQL as the database.

$ docker-compose run rails-web rails new . --force --database=postgresql

On our docker-compose.yml file on rails-web service, we define build tag with value equals to ‘.’ (dot). It means we will build our rails-web container using the default Dockerfile file. Then it runs

$ rails new . --force --database=postgresql

Remember we copied Gemfile and Gemfile.lock files to container directory? By using ruby Gemfile, we can specify the rails version to be installed. We need to add — forcehere to override our defined Gemfile, Gemfile.lock and .ruby-version files.

Sixth, create our containers

After rails installation succeed, we need to create our containers by running

$ docker-compose build

And run our created containers

$ docker-compose up or

$ docker-compose up -d if we want to run our docker as a background task.

Seventh, adjust database credentials

Edit our config/database.yml file to use our defined PostgreSQL environment in the docker-compose.yml file. Generally, Docker will set up the database host at 172.17.0.1.

If you need to use the env file, I recommend installing the dotenv-rails gem. Read here for official documentation of dotenv-rails:

To install the dotenv-rails gem, just open Gemfile file and add

gem 'dotenv-rails' and then run

$ docker-compose run rails-web bundle install and

$ docker exec -it rails-web bundle install

After adding dotenv-rails gem (optional), we can create our required databases. We can run:

$ docker exec -it rails-web rake db:create

Or if we can go to our container terminal by typing

$ docker exec -it rails-web /bin/bash

And then run rake db:create

Eighth, run our created Rails app

Open a web browser and type
0.0.0.0:3000 or localhost:3000

And a warm congratulatory image will appear

Additional Information

If we need to use webpacker to compile our assets, first we need to run
$ rails webpacker:install
This will add node_modules/ folder and add other required files & folders.

To do this, type:
$ docker exec -it rails-web rails webpacker:install
Or enter rails-web console
$ docker exec -it rails-web /bin/bash
and run
$rails webpacker:install

To compile our assets, we need to run webpack server. For development environment, we can run this on rails-web console
$ ./bin/webpack-dev-server
And it will be run on port 3035.

This is the end of this lengthy tutorial (^_^)

Now you have a fresh Rails 6 app with PostgreSQL already added.
Ready to make any Rails-based projects?

If you enjoyed this post, I’d be grateful if you’d help it spread by sharing this with a friend, or on social media. Thank you :)

--

--

Norfa Bagas
Norfa Bagas

Written by Norfa Bagas

Software engineer. I also do some research related to Computer Vision and Artificial Intelligence

No responses yet