Running Rails applications in a container makes managing configurations easy on my device and cross platform compatibility really easy for code reviews.
This post goes into containerizing a new rails applicaiton. It is a very “in the weeds” techincal post. Also after looking at the way I formatted this post, I would not recommend using this kind of setup for a new rails setup. I would use chruby or rvm. I have used both for development and they are both good, but currently I prefer chruby. There are benefits and drawbacks to everything.
Rails in a Container from Scratch
From my linux machine I ran
mkdir dds and then created a Dockerfile with
The contents of the Dockerfile look like this:
FROM ruby:2.6.0 RUN apt-get update && apt-get install -y \ build-essential RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \ && apt-get install -y nodejs RUN mkdir -p /app WORKDIR /app RUN gem install rails RUN rails new .
I then built the dockerfile into an image and tagged it using
docker build -t rails:dds .: note
-t for tag and the
. for using this folders Dockerfile.
I then created a container named with
docker create --name dds_rails rails:dds with the docker image I just created. This command then returned a container id for the container. The container id is a UUID generated by the Docker application.
docker container ls -a to find the container id, as
docker container ls only shows the running containers.
I then ran
docker cp -a container_id:/app . to copy the application code down into a folder on my host named
app. I refreshed the folder on my local machine and the application code from the intermediary container copied into a new folder named
./app on my host machine.
Back to Dockerfile
Now that I had the start to a rails project on my computer, I went back to my Dockerfile and made some revisions. The dockerfile from above was more of a big bang and was and wil only be used once. It needs to be revised to host the application.
I changed the Dockerfile to look like:
FROM ruby:2.6.0 RUN apt-get update && apt-get install -y \ build-essential vim RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - \ && apt-get install -y nodejs RUN mkdir -p /app WORKDIR /app ENV EDITOR=vim COPY Gemfile Gemfile.lock ./ RUN gem install bundler && bundle install --jobs 20 --retry 5 COPY . ./ EXPOSE 3000 CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
Then I moved this
Dockerfile into the
app directory that has just been copied into your working directory.
Change directories into the
app folder and
docker build -t rails:app-name . to create the image. You will then need to create the container like we did before with
docker create --name dds-app rails:app-name, note we used the
rails:app-name tag because this is the image we gave to our new Dockerfile image.
docker run -it -p 3000:3000 rails:dds we will be running our application on our host machine from a temporary container on port 3000.
A quick update is the ENV EDITOR=vim line and the ..
apt-get build-essential vim with the vim package added/ installed.
This will allow us to edit secret credentials using the
/usr/local/bundle/bin/rails credentials:edit command.
Run Rails Commands from the host using commands from the Container with a Volume
To run all rails commands and create controllers and models, you will need to be in the container and copy down the changes as you make them. This is a little inconvient for now, but I will be using the container’s
/usr/local/bundle/bin/rails command from the host machine and a bash alias to copy the changes down.
I crafted up this
docker run -it -p 3000:3000 -v $(pwd):/app rails:dds /usr/local/bundle/bin/rails generate controller static to generate a static controller as a test, but I think I will alias the first part of this command,
docker run -it -v $(pwd):/app rails:dds /usr/local/bundle/bin/rails to something easier on my local development machine like
drails.. Note this
drails will only work from the present working directory of the rails application.. From my bash terminal in my Mac, I ran
alias drails=docker run -it -v $(pwd):/app rails:dds /usr/local/bundle/bin/rails to create the drails alias. Note the
$(pwd) will give you trouble if you have a file or directory that has spaces. You will have to swap the
$(pwd) with the full location of the
With this alias configured, we can run
drails g model test and this will create a new model. With this we have access to all the normal
I then created essentially the same alias for the rest of the container binaries, but I may go back and write a bash function to run anything from the container.
We are now able to run everything from our local machine without having to install ruby or rails on our machine. We can then run the rails application with
docker run -t -p 3000:3000 rails:dds from the host machine and our host will have our rails application running on port 3000.
I opened my browser and everything appeared to be working well.
If I were to redo this, I think I would have modified the second dockerfile in this post to allow me to attach to the application container and run the commands in the container and using the attached
host:container volume I would be able to modify the files on my host machine.
It is really inconvienent to run rails commands from the host to the container using the
drails alias I created, but I am going to work on a simple bash function to run all the container binaries (everything in the
/usr/local/bundle/bin/ folder). This will be covered in the second part of this post.
Checkout Part 2 for more on bash scripts around docker. Part 3 ofwill deal with deploying a containerized rails site to production.