Skip to content

Instantly share code, notes, and snippets.

@bondarolik
Last active April 16, 2022 19:40
Show Gist options
  • Save bondarolik/0991b560decec3ff7cf52bb8fa13c715 to your computer and use it in GitHub Desktop.
Save bondarolik/0991b560decec3ff7cf52bb8fa13c715 to your computer and use it in GitHub Desktop.
VPS setup for Ruby on Rails with 2 environments

Create new droplet (or Amazon Instance)

Use Ubuntu 20+ (any LTS version). Recommended hardware for production: 4CPU/ RAM 8Gb / SSD 80Gb

For staging could less power.

If you need, prepare Bucket S3/Spaces and RDS database droplet/instance.

Prepare your VPS

Add deploy user and setup ssh keys

Login as root in newely created instance. Run any system updates available;

apt-get update
apt-get upgrade -y

Next, create user. Still on root account:

adduser deploy
adduser deploy sudo

Then, enter as deploy user and navigate to a home directory where continue working on.

su deploy
cd ~
pwd
# =>
/home/deploy

Setup ssh and authorized keys. Copy your ssh to text file, you will need it later.

ssh-keygen
cat .ssh/id_rsa.pub

Copy your local Pub key to server. If you're on Mac can use ssh-copy-id. Or copy it manually to authorized_keys file insdie of .ssh directory

Configure proper folder and file permissions:

chmod -R go= ~/.ssh
chown -R deploy:deploy ~/.ssh

Last step is to add deploy user to sudoers. Exit to root account and run visudo command:

exit

# from root account
visudo

At the end of file before #includedir /etc/sudoers.d add your deploy user like this:

deploy ALL=(ALL) NOPASSWD:ALL

Now, try login to your instance with deploy user and run any sudo command:

ssh deploy@216.238.106.224
sudo apt-get update

You should be able to id withour sudo password. Now, from here you should use only deploy user and sudo command.

Disabling Password Authentication on Your Server

In your server edit /etc/ssh/sshd_config file. Open it and find the lines PasswordAuthentication yes and PermitRootLogin yes. Change it to no if needed. That will disable login to your server as root and with password. Only pub-key allowed from now. Then restart your shell:

sudo systemctl restart ssh

Configuring a Basic Firewall

Follow this steps. Enable basic and standart ports (80, 25, 443, 21) and/or any other ports at your needs:

sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 25/tcp
sudo ufw allow 443/tcp
sudo ufw allow 21/tcp
sudo ufw show added
sudo ufw enable

Configure Timezones and Network Time Protocol Synchronization

The first step will ensure that your server is operating under the correct time zone. The second step will configure your system to synchronize its system clock to the standard time maintained by a global network of NTP servers. This will help prevent some inconsistent behavior that can arise from out-of-sync clocks.

sudo dpkg-reconfigure tzdata
# result:
Current default time zone: 'America/Argentina/Buenos_Aires'
Local time is now:      Fri Apr 15 15:50:54 -03 2022.
Universal Time is now:  Fri Apr 15 18:50:54 UTC 2022.
sudo apt-get update
sudo apt-get install ntp -y

Create a Swap File

Adding “swap” to a Linux server allows the system to move the less frequently accessed information of a running program from RAM to a location on disk. Accessing data stored on disk is much slower than accessing RAM, but having swap available can often be the difference between your application staying alive and crashing. This is especially useful if you plan to host any databases on your system.

Allocate the space you want to use for your swap file using the fallocate utility. For example, if we need a 8 Gigabyte file, we can create a swap file located at /swapfile by typing:

sudo fallocate -l 8G /swapfile

Posible you already have a swap:

If you met with fallocate: fallocate failed: Text file busy message, then your VPS is already have a swap. To ensure type this:

sudo swapon --show

# Result should be similar to this:

NAME      TYPE SIZE USED PRIO
/swapfile file 7.4G   0B   -2

If not, contact the Tech Support of your Provider.

If no errors happened, then continue to next steps:

After creating the file, we need to restrict access to the file so that other users or processes cannot see what is written there:

sudo chmod 600 /swapfile

We now have a file with the correct permissions. To tell our system to format the file for swap, we can type:

sudo mkswap /swapfile

Now, tell the system it can use the swap file by typing:

sudo swapon /swapfile

Our system is using the swap file for this session, but we need to modify a system file so that our server will do this automatically at boot. You can do this by typing:

sudo sh -c 'echo "/swapfile none swap sw 0 0" >> /etc/fstab'

Note Your VPS is ready! Take a Snapshot of your Current Configuration

Install Rbenv and Ruby

Install Node, Yarn and other dependencies

Adding Node.js repository

curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -

Adding Yarn repository

curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo add-apt-repository ppa:chris-lea/redis-server

Refresh our packages list with the new repositories

sudo apt-get update

Install our dependencies for compiiling Ruby along with Node.js and Yarn

sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev dirmngr gnupg apt-transport-https ca-certificates redis-server redis-tools nodejs yarn

Next we're going to install Ruby using a Ruby version mmanager called rbenv. It is the easiest and simplest option, plus it comes with some handy plugins to let us easily manage environment variables in production.

Note Change Ruby version to desired

git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
git clone https://github.com/rbenv/rbenv-vars.git ~/.rbenv/plugins/rbenv-vars
exec $SHELL
rbenv install 3.1.1
rbenv global 3.1.1
ruby -v
# ruby 3.1.1

The last step is to install Bundler. Install the version you use at the project. But before that, let's avoid GEM install documentation:

# Create gemrc file
echo 'gem: --no-document' > ~/.gemrc

# Install latest version of Bundler
gem install bundler

# Install specified version
gem install bundler -v 1.17.3

bundle -v
# Bundler version 1.17.3

Install Nginx web server & Passenger

For production, we'll be using NGINX as our webserver to receive HTTP requests. Those requests will then be handed over to Passenger which will run our Ruby app.

Installing Passenger is pretty straightforward. We'll add their repository and then install and configure their packages.

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7

sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger focal main > /etc/apt/sources.list.d/passenger.list'

sudo apt-get update

sudo apt-get install -y nginx-extras libnginx-mod-http-passenger

if [ ! -f /etc/nginx/modules-enabled/50-mod-http-passenger.conf ]; then sudo ln -s /usr/share/nginx/modules-available/mod-http-passenger.load /etc/nginx/modules-enabled/50-mod-http-passenger.conf ; fi

sudo ls /etc/nginx/conf.d/mod-http-passenger.conf

Now that we have NGINX and Passenger installed, we need to point Passenger to the correct version of Ruby.

which ruby
#=> /home/deploy/.rbenv/shims/ruby

sudo nano /etc/nginx/conf.d/mod-http-passenger.conf

# We simply want to change the passenger_ruby line to match the following:
passenger_ruby /home/deploy/.rbenv/shims/ruby;

Save this file and we'll start NGINX.

sudo service nginx start

You can check and make sure NGINX is running by visiting your server's public IP address in your browser and you should be greeted with the "Welcome to NGINX" message.

Note. Before continue I suggest create another snapshot

Configure Virtual Host for RoR Application

Next we're going to remove this default NGINX server and add one for our application instead.

# remove default VH
sudo rm /etc/nginx/sites-enabled/default

# Add your own and point to RoR app folder
sudo nano /etc/nginx/sites-enabled/appname.conf

Change RoR directory and put this in sites-enabled/appname.conf

server {
  listen 80;
  listen [::]:80;

  server_name _;
  root /home/deploy/appname/current/public;

  passenger_enabled on;
  passenger_app_env production;

  location /cable {
    passenger_app_group_name appname_websocket;
    passenger_force_max_concurrent_requests_per_process 0;
  }

  # Allow uploads up to 100MB in size
  client_max_body_size 100m;

  location ~ ^/(assets|packs) {
    expires max;
    gzip_static on;
  }
}

Save the file and then we'll reload NGINX to load the new server files.

sudo service nginx reload

Install Database

I recommend PostgreSQL for your production database but feel free to use MySQL instead.

For Postgres, we're going to start by installing the Postgres server and libpq which will allow us to compile the pg rubygem.

Then, we're going to become the postgres linux user who has full access to the database and use that account to create a new database user for our apps. We'll call that user deploy.

And finally, the last command will create a database called myapp and make the deploy user owner.

Be careful typing password

sudo apt-get install postgresql postgresql-contrib libpq-dev
sudo su - postgres
createuser --pwprompt deploy
createdb -O deploy app_name_db
exit

Misc

Install imagemagick

sudo apt-get install imagemagick -y

Redis

sudo apt-get update
sudo apt install redis-server

Configure Redis

sudo nano /etc/redis/redis.conf

Next, find the line specifying the supervised directive. By default, this line is set to no. However, to manage Redis as a service, set the supervised directive to systemd (Ubuntu’s init system).

Next, scroll down and find the NETWORK section in the file. Then, uncomment the bind 127.0.0.1 ::1 line (by removing #).

Save the changes and close the file and restart redis

sudo systemctl enable redis-server
sudo systemctl restart redis.service
sudo systemctl status redis

Check Redis connection

redis-cli
PING
# => PONG
exit

Deploy your code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment