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.
Top comments (2)
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.
What is dockerfile dependencies for rmagick?