DEV Community

Roeland
Roeland

Posted on • Edited on

Deploy Rails 7 with Docker and Nginx

The easiest solution to deploy a Rails application is something like Heroku or Hatchbox or DigitalOcean Apps. But for some small projects I like to use my existing VPS.

I assume you have a VPS with Ubuntu 20.04 and Docker and Nginx installed.
If you use DigitalOcean you can select the Docker image in the marketplace and add Nginx to it.

New Rails 7 app

Lets create a new Rails project with PostgreSQL, esbuild and Tailwind on your local machine:

rails new demo -d postgresql --edge -j esbuild --css tailwind
Enter fullscreen mode Exit fullscreen mode

Adjust you config/database.yml with the settings for your database.

Scaffold a simple table:

bin/rails g scaffold Book name:string
Enter fullscreen mode Exit fullscreen mode

Then create your database and tables:

bin/rails db:create
bin/rails db:migrate
Enter fullscreen mode Exit fullscreen mode

You can create a root path in routes.rb:

root "books#index"
Enter fullscreen mode Exit fullscreen mode

Now you can start the website with:

bin/dev
Enter fullscreen mode Exit fullscreen mode

Docker

Let's go to the VPS. I transfer my code with Github.

For large projects you would probably use CI, but this is just a small project.

I prefer to create a small shell script to do the build steps and start rails.
This is the content of bin/prod:

#!/usr/bin/env bash
export RAILS_ENV=production
bundle install
yarn install
yarn build
yarn build:css
bin/rails assets:precompile
bin/rails server -b 0.0.0.0
Enter fullscreen mode Exit fullscreen mode

Let's make it executable with: chmod a+x bin/prod

Now create a Dockerfile:

FROM ruby:3
RUN apt-get update -qq && apt-get install -y nodejs npm postgresql-client
RUN npm install -g yarn
RUN gem update --system

# use a global path instead of vendor
ENV GEM_HOME="/usr/local/bundle"
ENV BUNDLE_PATH="$GEM_HOME"
ENV BUNDLE_SILENCE_ROOT_WARNING=1
ENV BUNDLE_APP_CONFIG="$GEM_HOME"
ENV PATH="$GEM_HOME/bin:$BUNDLE_PATH/gems/bin:${PATH}"

# make 'docker logs' work
ENV RAILS_LOG_TO_STDOUT=true

# copy the source
WORKDIR /app
COPY . /app
RUN rm -f tmp/pids/server.pid
RUN bundle install

# build and start
CMD ["bin/prod"]
Enter fullscreen mode Exit fullscreen mode

The master.key file is not in git for safety.
There are several solutions for this, but I just recreate the file on the server:

echo "30acf9tralalalalala7af75eb7" > config/master.key
Enter fullscreen mode Exit fullscreen mode

Now it's time to create the docker image.

Run this inside the root folder of the demo project:

docker build -t demo:0.0.1 .
Enter fullscreen mode Exit fullscreen mode

You should now see the image with docker images.

Lets run it:

docker run -d -p 3001:3000 --name demo --env RAILS_ENV=production -v ~/demo:/app demo:0.0.1
Enter fullscreen mode Exit fullscreen mode

The docker container is exposing port 3000, but I map that to 3001 since I already have an other website running on port 3000.

You should probably have a seperate Postgres server, but I also run that inside a Docker.
To allow access to this container I create a seperate network and add the two containers in it.
In database.yml you can than use postgres_container as host.

docker network create demo_network
docker network connect demo_network demo
docker network connect demo_network postgres_container
Enter fullscreen mode Exit fullscreen mode

To create the database and tables:

docker exec demo bin/rails db:create
docker exec demo bin/rails db:migrate
Enter fullscreen mode Exit fullscreen mode

In case of errors you can use docker logs demo to find the error.

Nginx

I use Nginx as a proxy to the different Rails projects and to load the assets directly.
To create a new configuration:

sudo vi /etc/nginx/sites-available/demo
Enter fullscreen mode Exit fullscreen mode

And this is the content: (you need to change the domain and paths)

upstream demo {
  server localhost:3001;
}

server {
  server_name demo.example.org;

  root /home/user/demo/public;
  access_log /home/user/demo/log/nginx.access.log;
  error_log /home/user/demo/log/nginx.error.log info;

  location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
  }

  try_files $uri/index.html $uri @demo;
  location @demo {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header  X-Forwarded-Proto $scheme;
    proxy_set_header  X-Forwarded-Ssl on; # Optional
    proxy_set_header  X-Forwarded-Port $server_port;
    proxy_set_header  X-Forwarded-Host $host;

    proxy_redirect off;

    proxy_pass http://demo;
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 100M;
  keepalive_timeout 10;
}
Enter fullscreen mode Exit fullscreen mode

Enable it:

sudo ln -s /etc/nginx/sites-available/demo /etc/nginx/sites-enabled/demo
Enter fullscreen mode Exit fullscreen mode

Test the configuration:

sudo nginx -t
Enter fullscreen mode Exit fullscreen mode

And restart Nginx:

sudo systemctl restart nginx
Enter fullscreen mode Exit fullscreen mode

You should now have a working website.
It's a good idea to add Let's Encrypt with the certbot tool.
This is explained here.

Latest comments (2)

Collapse
 
developerbishwas profile image
Bishwas Bhandari

What is dockerfile dependencies for rmagick?

Collapse
 
philsmy profile image
Phil Smy

Am I missing something? How do you get your code onto the VPS? Where are you putting the code on the VPS? How do you configure your VPS?

Seems like a large chunk is not described.