Roeland
Posted on October 3, 2021
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
Adjust you config/database.yml with the settings for your database.
Scaffold a simple table:
bin/rails g scaffold Book name:string
Then create your database and tables:
bin/rails db:create
bin/rails db:migrate
You can create a root path in routes.rb:
root "books#index"
Now you can start the website with:
bin/dev
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
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"]
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
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 .
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
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
To create the database and tables:
docker exec demo bin/rails db:create
docker exec demo bin/rails db:migrate
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
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;
}
Enable it:
sudo ln -s /etc/nginx/sites-available/demo /etc/nginx/sites-enabled/demo
Test the configuration:
sudo nginx -t
And restart Nginx:
sudo systemctl restart nginx
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.
Posted on October 3, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.