Konnor Rogers
Posted on May 3, 2020
Getting Started with Rails 6 and Docker
This post is best viewed on my Blog site.
https://paramagicdev.github.io/my-blog/rails/getting-started-with-rails-6/
Purpose
The purpose of this is to have a reusable source for setting up a Rails
6 project.
I will be using the Getting Started with Rails Guide to setup a
new Rails projects.
Initially I used Docker Quickstart with Compose and Rails guide but quickly realized I
had other needs for Rails 6. My docker setup is the result of multiple
resources.
I will also be using Docker just to provide a consistent development
environment. Docker is not required, I used it simply to be able to
provide a reproducible environment.
Source code can be found here:
https://github.com/ParamagicDev/getting-started-with-rails-6
Deployed application can be found here:
https://getting-started-with-rails-6.herokuapp.com/
Table Of Contents
Prerequisites
Make sure to install both Docker and Docker Compose prior to starting
this tutorial.
To verify run the following:
docker -v
# Docker version 19.03.8
docker-compose -v
# docker-compose version 1.25.0
Main Technologies
- Ruby 2.5.8
- Rails 6.0.2
- PostgresQL 11.6
Getting Started
If you don't want any explanations, skip to the I know what I'm doing
section of this post.
Alright first lets create our directory where we want the Rails app. I
named mine getting-started-with-rails-6
mkdir getting-started-with-rails-6
cd getting-started-with-rails-6
Adding a Dockerfile
The next step is to create our Dockerfile
.
The below Dockerfile
is modified from the Docker Quickstart
Rails
# Dockerfile
# Pre setup stuff
FROM ruby:2.5.8 as builder
# Add Yarn to the repository
RUN curl https://deb.nodesource.com/setup_12.x | bash && curl https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
# Install system dependencies & clean them up
RUN apt-get update -qq && apt-get install -y \
postgresql-client build-essential yarn nodejs \
libnotify-dev && \
rm -rf /var/lib/apt/lists/*
# This is where we build the rails app
FROM builder as rails-app
# Allow access to port 3000
EXPOSE 3000
EXPOSE 3035
# This is to fix an issue on Linux with permissions issues
ARG USER_ID=1000
ARG GROUP_ID=1000
ARG APP_DIR=/home/user/myapp
# Create a non-root user
RUN groupadd --gid $GROUP_ID user
RUN useradd --no-log-init --uid $USER_ID --gid $GROUP_ID user --create-home
# Remove existing running server
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
# Permissions crap
RUN mkdir -p $APP_DIR
RUN chown -R $USER_ID:$GROUP_ID $APP_DIR
# Define the user running the container
USER $USER_ID:$GROUP_ID
WORKDIR $APP_DIR
# Install rails related dependencies
COPY --chown=$USER_ID:$GROUP_ID Gemfile* $APP_DIR/
# For webpacker / node_modules
COPY --chown=$USER_ID:$GROUP_ID package.json $APP_DIR
COPY --chown=$USER_ID:$GROUP_ID yarn.lock $APP_DIR
RUN bundle install
# Copy over all files
COPY --chown=$USER_ID:$GROUP_ID . .
RUN yarn install --check-files
ENTRYPOINT ["/usr/bin/entrypoint.sh"]
# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
Adding a Gemfile
Next we will deviate slightly from the above Docker quickstart. Instead
of using Rails 5 we'll use Rails 6.
Create a Gemfile
with the following contents:
# Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 6'
Then add an empty Gemfile.lock
touch Gemfile.lock
Adding a package.json
There are a few options to generate your package.json
so lets keep it
simple, create a file with the following settings:
{
"_filename": "package.json",
"name": "myapp",
"private": true,
"version": "0.1.0"
}
Also, add an empty yarn.lock
because Rails uses yarn by default.
touch yarn.lock
Adding entrypoint.sh
Now lets create an entrypoint.sh
script to fix a server issue with
Rails.
#!/bin/bash
# entrypoint.sh
set -e
# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid
# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
Adding docker-compose.yml
Finally, lets add a docker-compose.yml
with the following content:
# docker-compose.yml
version: "3"
services:
web:
environment:
NODE_ENV: development
RAILS_ENV: development
WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
POSTGRES_USER: postgres
POSTGRES_PASSWORD: example
build:
context: .
args:
USER_ID: 1000
GROUP_ID: 1000
APP_DIR: /home/user/myapp
command: bash -c "rm -f tmp/pids/server.pid &&
./bin/webpack-dev-server &
bundle exec rails server -p 3000 -b '0.0.0.0'"
volumes:
# make sure this lines up with APP_DIR above
- .:/home/user/myapp
ports:
- "3000:3000"
- "3035:3035"
depends_on:
- db
db:
image: postgres:12.2
environment:
POSTGRES_PASSWORD: example
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
Adding a .dockerignore file
Finally, its good practice to add a .dockerignore
file. The
.dockerignore
is very similar to .gitignore
and this one will very
closely resemble your .gitignore
that Rails will generate.
Create a .dockerignore
file with the following contents:
# .dockerignore
# Ignore bundler config.
/.bundle
# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep
# Ignore pidfiles, but keep the directory.
/tmp/pids/*
!/tmp/pids/
!/tmp/pids/.keep
# Ignore uploaded files in development.
/storage/*
!/storage/.keep
/public/assets
.byebug_history
# Ignore master key for decrypting credentials and more.
/config/master.key
/public/packs
/public/packs-test
/node_modules
/yarn-error.log
yarn-debug.log*
Prebuild Directory Structure
Your directory should look as follows:
.
├── docker-compose.yml
├── .dockerignore
├── Dockerfile
├── entrypoint.sh
├── Gemfile
├── Gemfile.lock
├── package.json
└── yarn.lock
For reference, I have created a Github branch to represent the file
structure.
Prebuild Reference Repository Branch
Building the Project
Create the Rails app
Prior to building the docker container, you have to create the Rails app
structure. To do so, run the command below inside of your Rails project
directory.
docker-compose run --rm --no-deps web rails new . --force --no-deps --database=postgresql
This will build a fresh Rails project for you using PostgresQL
as the
database adapter.
Postbuild Directory Structure
Your Rails directory should look as follows:
.
├── app
├── babel.config.js
├── bin
├── .browserslistrc
├── config
├── config.ru
├── docker-compose.yml
├── .dockerignore
├── Dockerfile
├── entrypoint.sh
├── Gemfile
├── Gemfile.lock
├── .git
├── .gitignore
├── lib
├── log
├── package.json
├── postcss.config.js
├── public
├── Rakefile
├── README.md
├── .ruby-version
├── storage
├── test
├── tmp
├── vendor
└── yarn.lock
Directory Structure after Rails new
Ownership Issues
You may run into ownership issues on Linux. I did my best to fix this.
In case anything still lingers, run the following:
sudo chown -R "$USER":"$USER" .
And if you're feeling real crazy, you can setup an alias
for this
command. I have mine called ownthis
alias ownthis="sudo chown -R $USER:$USER ."
Connecting the Database
In order to connect the Database to Rails, you have to tell Rails where
to find the database. To do so, navigate to your config/database.yml
file.
Delete the contents of your config/database.yml
and add the following:
# config/database.yml
default: &default
adapter: postgresql
encoding: unicode
host: db
username: <%= ENV['POSTGRES_USER'] %>
password: <%= ENV['POSTGRES_PASSWORD'] %>
pool: 5
development:
<<: *default
database: myapp_development
test:
<<: *default
database: myapp_test
Now you can boot the app using the following command:
docker-compose up --build
In another terminal, run the following commands:
docker-compose run --rm web rails db:create
docker-compose run --rm web rails db:migrate
Congratulations! You have finished the setup portion of the application!
Now you should be able to view your app by navigating to:
localhost:3000
in your browser's address bar.
You should see a message congratulating you for using Rails.
Github Branch Prior to adding additional functionality
Using Docker
Stopping the application
Using Docker
Stopping the application
To stop the application, in another terminal simply run:
docker-compose down
Starting the application
To start the application there are two methods.
If you have added anything to the Gemfile
, in order to sync the
changes, you must run the following:
docker-compose run web bundle install
docker-compose up --build
If you have not changed anything Gemfile
related but you may have
changed the docker-compose.yml
file, you can simply run:
docker-compose up --build
However, if you do not need to rebuild, you can simply run:
docker-compose up
Extra Tips
As a simple way to get you going, anytime you see
rails [command]
simply prepend the following:
docker-compose run --rm web rails [command]
docker-compose exec
is to be run if you have a container already
running.
docker-compose run
is to be run if you do not have a container
running.
docker-compose run --rm
will automatically remove the docker instance
once the command finished
Useful Commands
# builds a container
docker-compose build
# starts a container thats been built (equivalent to `rails server`)
docker-compose up
# starts and builds a container
docker-compose up --build
# runs a one-off instance
docker-compose run --rm web [command]
# runs a command inside of a running container
# `docker-compose up` needs to be running in another terminal
docker-compose exec web [command]
# stops the application
docker-compose down
# Remove orphaned containers as well
docker-compose down --remove-orphans
# run a bash instance inside of the docker-compose container
# now you can simply run commands like `rails db:migrate` without
# adding `docker-compose run web` before every command
docker-compose run --rm web /bin/bash
# Things are totally jacked up? Remove all images and containers.
# https://stackoverflow.com/a/52179797
docker rm $(docker ps -q -a) -f && docker rmi $(docker images -q) -f
Adding additional functionality
In an effort to keep this blog post semi-short in length, I will refer
you to the Rails guide for this part as nothing will be different. Once
you're finished going through the Rails guide, come back here and we
will deploy to Heroku!
Ruby on Rails Guide to Getting
Started
You can skip to section 4.2 because everything prior to that we have
just done above.
Deployment to Heroku
First, lets create a Heroku account. To do so, head on over to their
signup page.
After creating an account, install the Heroku CLI.
Installation instructions can be found
here.
Now that you have Heroku CLI installed you can login via terminal.
heroku login
# heroku: Enter your Heroku credentials
# Email: schneems@example.com
# Password:
# Could not find an existing public key.
# Would you like to generate one? [Yn]
# Generating new SSH public key.
# Uploading ssh public key /Users/adam/.ssh/id_rsa.pub
After you have logged in, you can now create a Heroku dyno
. Basically
what this means is they will provision a server for you to host your
site. To do this, simply run the following:
heroku apps:create <Your-app-name>
# Creating ⬢ <Your-app-name>... done
# https://<Your-app-name>.herokuapp.com/ |
https://git.heroku.com/<Your-app-name>.git
Now deployment is as simple as:
git push heroku master
After waiting a little bit you should see something like the following:
remote: -----> Launching...
remote: Released v6
remote: https://<Your-app-name>.herokuapp.com/ deployed to Heroku
To visit your site, simply run:
heroku open
However, youre not done yet! If you got to your site and go visit the
/articles
section, you will run into an error. This is because you
have not migrated the database on Heroku. To do so, run the following:
heroku run rails db:migrate
Now you're done! Good luck with everything and I hope this was helpful!
Issues
Problems with ownership?
sudo chown -R "$USER":"$USER" .
Things not working as expected?
docker-compose down --remove-orphans
docker-compose up --build
Tired of the yarn install --check-files
issues?
Disable it!
# config/webpacker.yml
# ...
check_yarn_integrity: false
# ...
Alternatively, run the following to fix this issue:
docker-compose run --rm web yarn install --check-files
No space left on device??
https://success.docker.com/article/error-message-no-space-left-on-device-in-default-machine
Postgres not updating a new name / passsword? You must first delete its
volume to tell postgres to rebuild it.
docker volume ls # lists the volumes
docker volume rm <volume-name> # removes the volume
docker volume prune [--force] # remove all unused volumes
I know what I'm doing.
This section is meant to be the TLDR version of the above.
This will move quickly and is meant more as a reference.
To skip this section, click on the below link:
mkdir -p new-rails-app
cd new-rails-app
touch Dockerfile docker-compose.yml entrypoint.sh \
Gemfile Gemfile.lock yarn.lock package.json \
.dockerignore
# Dockerfile
# Pre setup stuff
FROM ruby:2.5.8 as builder
# Add Yarn to the repository
RUN curl https://deb.nodesource.com/setup_12.x | bash && curl https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
# Install system dependencies & clean them up
RUN apt-get update -qq && apt-get install -y \
postgresql-client build-essential yarn nodejs \
libnotify-dev && \
rm -rf /var/lib/apt/lists/*
# This is where we build the rails app
FROM builder as rails-app
# Allow access to port 3000
EXPOSE 3000
EXPOSE 3035
# This is to fix an issue on Linux with permissions issues
ARG USER_ID=1000
ARG GROUP_ID=1000
ARG APP_DIR=/home/user/myapp
# Create a non-root user
RUN groupadd --gid $GROUP_ID user
RUN useradd --no-log-init --uid $USER_ID --gid $GROUP_ID user --create-home
# Remove existing running server
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
# Permissions crap
RUN mkdir -p $APP_DIR
RUN chown -R $USER_ID:$GROUP_ID $APP_DIR
# Define the user running the container
USER $USER_ID:$GROUP_ID
WORKDIR $APP_DIR
# Install rails related dependencies
COPY --chown=$USER_ID:$GROUP_ID Gemfile* $APP_DIR/
# For webpacker / node_modules
COPY --chown=$USER_ID:$GROUP_ID package.json $APP_DIR
COPY --chown=$USER_ID:$GROUP_ID yarn.lock $APP_DIR
RUN bundle install
# Copy over all files
COPY --chown=$USER_ID:$GROUP_ID . .
RUN yarn install --check-files
ENTRYPOINT ["/usr/bin/entrypoint.sh"]
# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
# docker-compose.yml
version: "3"
services:
web:
environment:
NODE_ENV: development
RAILS_ENV: development
WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
POSTGRES_USER: postgres
POSTGRES_PASSWORD: example
build:
context: .
args:
USER_ID: 1000
GROUP_ID: 1000
APP_DIR: /home/user/myapp
command: bash -c "rm -f tmp/pids/server.pid &&
./bin/webpack-dev-server &
bundle exec rails server -p 3000 -b '0.0.0.0'"
volumes:
# make sure this lines up with APP_DIR above
- .:/home/user/myapp
ports:
- "3000:3000"
- "3035:3035"
depends_on:
- db
db:
image: postgres:12.2
environment:
POSTGRES_PASSWORD: example
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
#!/bin/bash
# entrypoint.sh
set -e
# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid
# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
# Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 6'
{
"_filename": "package.json",
"name": "myapp",
"private": true,
"version": "1.0.0"
}
# .dockerignore
# .dockerignore
# Ignore bundler config.
/.bundle
# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep
# Ignore pidfiles, but keep the directory.
/tmp/pids/*
!/tmp/pids/
!/tmp/pids/.keep
# Ignore uploaded files in development.
/storage/*
!/storage/.keep
/public/assets
.byebug_history
# Ignore master key for decrypting credentials and more.
/config/master.key
/public/packs
/public/packs-test
/node_modules
/yarn-error.log
yarn-debug.log*
After setting up the above files, then run:
docker-compose run --rm --no-deps web rails new . --force --no-deps --database=postgresql
Now run:
docker-compose build
After building the image, then install webpacker:
docker-compose run --rm web rails webpacker:install
This will provide you with a base for webpacker.
Now setup the database in the config/database.yml
# config/database.yml
default: &default
adapter: postgresql
encoding: unicode
host: db
username: <%= ENV['POSTGRES_USER'] %>
password: <%= ENV['POSTGRES_PASSWORD'] %>
pool: 5
development:
<<: *default
database: myapp_development
test:
<<: *default
database: myapp_test
Next create the database.
docker-compose run --rm web bash -c "rails db:create && rails db:migrate"
Finally, start the app:
docker-compose up
Now you can view it on localhost:3000
Now, to deploy the app, simply do the following:
heroku login
heroku apps:create <App-name>
git push heroku master
heroku run rails db:migrate
And thats it ! Were all set and deployed.
Links
Rails
Ruby on Rails Getting Started Guide
Docker
PostgresQL
Heroku
Found something wrong? Submit a pull request!
Posted on May 3, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.