I typically build and install my experimental applications to Heroku as it's a quick and easy way to deploy Ruby on Rails applications to production. This is especially useful when my apps are usually early prototypes of various ideas. However, for one of my side projects, I decided to host this particular prototype on my home server and felt the most logical route was to build a Docker container for my app. I found myself poking around a number of different sites to finally deploy my Rails 6.1 app. Here are the summarized steps
Setting up the Rails app
I had to make a couple of changes to the Rails app.
Change the following in your /config/environment/production.rb file
config.require_master_key = true
Change the following in your config/database.yml file
production: <<: *default url: <%= ENV['DATABASE_URL'] %>
Create Docker files
I decided to put the Docker file in the root directory of my Rails app and here's the content of the Dockerfile I created
FROM ruby:3.0.1 # replace shell with bash so we can source files RUN rm /bin/sh && ln -s /bin/bash /bin/sh RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs ghostscript RUN mkdir -p /app RUN mkdir -p /usr/local/nvm WORKDIR /app RUN curl -sL https://deb.nodesource.com/setup_14.x | bash - RUN apt-get install -y nodejs RUN node -v RUN npm -v # Copy the Gemfile as well as the Gemfile.lock and install # the RubyGems. This is a separate step so the dependencies # will be cached unless changes to one of those two files # are made. COPY Gemfile Gemfile.lock package.json ./ RUN gem install bundler RUN bundle install --verbose --jobs 20 --retry 5 RUN npm install -g yarn RUN yarn install --check-files # Copy the main application. COPY . ./ # Expose port 3000 to the Docker host, so we can access it # from the outside. EXPOSE 3000 # Pre-compile web assets RUN bundle exec rails webpacker:compile RUN bundle exec rails assets:precompile # The main command to run when the container starts. Also # tell the Rails dev server to bind to all interfaces by # default. CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
A couple of things to note.
- I'm currently using Node version 14 as that's the latest version at the time I created the app. You might want to update this for a newer version in the future
- I first tried to run bundle install with just the production gems but found that there was a whole slew of gems from the development section that it needed to run the app. Found that it was easier to just run the full bundle install instead.
You can use a .dockerignore file to reduce any unnecessary bloat for your Docker image. A .dockerignore file works very similarly to a .gitignore file and most of the contents can be duplicated as the files that you don't need in your repositiory should also not be in your running application.
This is my .dockerignore file configuration
Once this is set up, you're ready to create your Docker image. To do this, run the following command from the project root folder as this is also where your Docker file lives
docker build -t [full url of where the image will be hosted]:[tag] .
This should result with a Docker image for your app. Now you're almost ready to run your docker container. Just before you do that, it's probably best to set up an environment variable file for the project as this allows you to minimize the amount of typing to run the container. I called mine docker.env and put it in the config directory of the app. Here's what I put in my file.
RAILS_MASTER_KEY=[from config/master.key file] RAILS_ENV=production RAILS_LOG_TO_STDOUT=true RAILS_SERVE_STATIC_FILES=true
Here are the things that you want to pay attention to
- The RAILS_MASTER_KEY variable allows your app to decrypt your credentials.yml file at run time.
- The RAILS_LOG_TO_STDOUT variable allows you to output your log to Docker so you can debug your app as necessary
- The RAILS_SERVER_STATIC_FILES variable allows you to pre-compile your assets so your app doesn't have to do it at run time.
Now that is done, you're ready to run your app by running the following command:
docker run --env-file config/tems_core.env -p 80:3000 --name [application name] [docker container url]:[tag]