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.
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.
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
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
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
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
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.
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
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
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
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
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
sudo apt-get install imagemagick -y
sudo apt-get update
sudo apt install redis-server
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
redis-cli
PING
# => PONG
exit