We should install a version manager to keep our ruby versions and gems.
sudo apt-get install libgdbm-dev libncurses5-dev automake libtool bison libffi-dev
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
curl -sSL https://get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm
rvm install 2.5.1
rvm use 2.5.1 --default
ruby -v
You should create a swap file if your system does not have enough memory.
# Create swapfile
sudo fallocate -l 1G /swapfile
# The resulting swap file is world-readable and proper permissions should be set to prevent
# unwanted access to this file
sudo chown root:root /swapfile
sudo chmod 0600 /swapfile
# Add the file to the swap system
sudo swapon /swapfile
# Format the file for swap
sudo mkswap /swapfile
# Adds line to the end of /etc/fstab to make the change permanent
sudo bash -c "echo '/swapfile none swap sw 0 0 ' >> /etc/fstab"
# Checks that swap file was created
sudo swapon -s
# Restart server
sudo reboot
To remove an old swapfile, use the following commands. The first command removes its entry from the /etc/fstab file.
sudo swapoff -v /swapfile
sudo rm /swapfile
user@ip:~⟫ du -hsc * /home/user/
105M file
4.0K file.txt
121M file_2
16K dir_1
866M /home/user/
1.1G total
user@ip:~⟫ df -h
Filesystem Size Used Avail Use% Mounted on
udev 488M 0 488M 0% /dev
tmpfs 100M 3.0M 97M 4% /run
/dev/xvda1 7.7G 5.2G 2.5G 68% /
tmpfs 496M 80K 496M 1% /dev/shm
tmpfs 5.0M 0 5.0M 0% /run/lock
tmpfs 496M 0 496M 0% /sys/fs/cgroup
tmpfs 100M 0 100M 0% /run/user/1000
Get the package and then run the secure installation.
sudo apt install mysql-server libmysqlclient-dev
sudo mysql_secure_installation
Add a database and database user.
mysql -u root -p
create database devdb;
create user 'devuser'@'localhost' identified by 'password';
grant all on devdb.* to 'devuser' identified by 'password';
grant all privileges on *.* to 'devuser'@'localhost';
ssh-keygen -t rsa -b 4096 -C "email@gmail.com"
vim .ssh/id_rsa.pub
git clone git@gitlab.com:user/app.git
Add your secrets and environment variables.
cd app/
vim config/secrets.yml
vim .env
sudo apt-get install nodejs
bundle install
rails db:migrate RAILS_ENV=production
rails assets:precompile RAILS_ENV=production
rails db:seed RAILS_ENV=production
sudo apt install apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt update
apt-cache policy docker-ce
sudo apt install docker-ce
sudo systemctl status docker
If you want to run docker with your user without having to use sudo, then do the following steps.
sudo usermod -aG docker ${USER}
su - ${USER}
id -nG
or
sudo usermod -aG docker username
Let's get the nginx package. This should be installed first before Passenger!
sudo apt-get install nginx
Let's install passenger and add the apt keys to our file.
sudo apt-get install -y dirmngr gnupg
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates
sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger bionic main > /etc/apt/sources.list.d/passenger.list'
sudo apt-get update
sudo apt-get install -y libnginx-mod-http-passenger
Check if the config files are in their place.
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
Check installation.
sudo /usr/bin/passenger-config validate-install
Check memory stats.
sudo /usr/sbin/passenger-memory-stats
sudo /usr/sbin/passenger-status
If you don't see a file at /etc/nginx/conf.d/mod-http-passenger.conf; then you need to create it yourself and set the passenger_ruby and passenger_root config options. Or for newer versions at /etc/nginx/passenger.conf.
passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini;
passenger_ruby /home/sam/.rvm/gems/ruby-2.5.1/wrappers/ruby;
Let's create a virtual host now.
sudo vim /etc/nginx/sites-available/portfolio
Add the following code to configure your website. If you want a different port, change the port number.
# /etc/nginx/sites-available/portfolio
server {
listen 4000;
listen [::]:4000;
passenger_enabled on;
root /home/sam/websites/portfolio/public;
server_name localhost;
}
Then create a symlink to your sites-enabled path.
sudo ln -s /etc/nginx/sites-available/portfolio /etc/nginx/sites-enabled/
Restart Nginx.
sudo service nginx restart
Follow the guide here.
Once you have puma installed, then create a file called puma.service in the systemd directory.
sudo vim /etc/systemd/system/puma.service
Copy and paste the following code.
[Unit]
Description=Puma HTTP Server
After=network.target
# Uncomment for socket activation (see below)
# Requires=puma.socket
[Service]
# Foreground process (do not use --daemon in ExecStart or config.rb)
Type=simple
# Preferably configure a non-privileged user
User=sam
# The path to the puma application root
# Also replace the "<WD>" place holders below with this path.
WorkingDirectory=/home/sam/websites/portfolio
# Helpful for debugging socket activation, etc.
# Environment=PUMA_DEBUG=1
# The command to start Puma. This variant uses a binstub generated via
# `bundle binstubs puma --path ./sbin` in the WorkingDirectory
# (replace "<WD>" below)
#ExecStart=<WD>/sbin/puma -b tcp://0.0.0.0:9292 -b ssl://0.0.0.0:9293?key=key.pem&cert=cert.pem
# Variant: Use config file with `bind` directives instead:
ExecStart=/home/sam/.rvm/wrappers/ruby-2.5.1/puma -C /home/sam/websites/portfolio/config/puma.rb
# Variant: Use `bundle exec --keep-file-descriptors puma` instead of binstub
Restart=always
[Install]
WantedBy=multi-user.target
Then enable the service.
sudo systemctl enable puma.service
Create a puma.conf file that will specify the location of each app to start on each line.
sudo vim /etc/puma.conf
# /etc/puma.conf
/home/sam/websites/portfolio
Then create your virtual host.
sudo vim /etc/nginx/sites-available/portfolio
Paste the following code.
# /etc/nginx/sites-available/portfolio
# Use localhost if you don't have a domain
upstream app {
server unix:/home/sam/websites/portfolio/shared/sockets/puma.sock fail_timeout=0;
}
server {
listen 80;
listen [::]:80;
server_name localhost;
try_files $uri/index.html $uri @app;
root /home/sam/websites/portfolio/public;
location @app {
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
location /cable {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
}
location ^~ /.well-known/ {
root /usr/share/nginx/html;
}
}
# /etc/nginx/sites-available/portfolio
server {
listen 80;
listen [::]:80;
server_name samwholst.com, www.samwholst.com;
return 301 https://samwholst.com$request_uri;
try_files $uri/index.html $uri @app;
root /home/sam/websites/portfolio/public;
location @app {
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
}
location /cable {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
}
location ^~ /.well-known/ {
root /usr/share/nginx/html;
}
}
server {
listen [::]:443 ssl; # managed by Certbot
listen 443 ssl; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
server_name www.samwholst.com;
ssl on;
ssl_certificate /etc/letsencrypt/live/www.samwholst.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/www.samwholst.com/privkey.pem; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
return 301 https://samwholst.com$request_uri;
}
server {
listen [::]:443 ssl; # managed by Certbot
listen 443 ssl; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl on;
ssl_certificate /etc/letsencrypt/live/www.samwholst.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/www.samwholst.com/privkey.pem; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
add_header Strict-Transport-Security "max-age=31536000; includeSubdomains; preload";
server_name samwholst.com;
root /home/sam/websites/portfolio/public;
try_files $uri/index.html $uri @app;
location @app {
proxy_pass http://app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host:443;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-Forwarded-Proto https;
}
location /cable {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_redirect off;
}
location ^~ /.well-known/ {
root /usr/share/nginx/html;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 365d;
}
location /status {
# If you want to remove /status from the URL, use rewrite.
rewrite /status(.*)$ $1 break;
proxy_pass http://${server_location};
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
}
}
Then create a symlink to the sites-enabled path.
sudo ln -s /etc/nginx/sites-available/portfolio /etc/nginx/sites-enabled/
Add a puma.rb file in your config directory of the app with the following code.
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port ENV.fetch("PORT") { 3000 }
# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }
# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked webserver processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory. If you use this option
# you need to make sure to reconnect any threads in the `on_worker_boot`
# block.
#
# preload_app!
# If you are preloading your application and using Active Record, it's
# recommended that you close any connections to the database before workers
# are forked to prevent connection leakage.
#
# before_fork do
# ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord)
# end
# The code in the `on_worker_boot` will be called if you are using
# clustered mode by specifying a number of `workers`. After each worker
# process is booted, this block will be run. If you are using the `preload_app!`
# option, you will want to use this block to reconnect to any threads
# or connections that may have been created at application boot, as Ruby
# cannot share connections between processes.
#
# on_worker_boot do
# ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
# end
#
# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart
# Change to match your CPU core count
workers 1
# Min and Max threads per worker
threads 1, 6
app_dir = File.expand_path("../..", __FILE__)
shared_dir = "#{app_dir}/shared"
# Default to production
rails_env = ENV['RAILS_ENV'] || "production"
environment rails_env
# Set up socket location
bind "unix://#{shared_dir}/sockets/puma.sock"
# Logging
stdout_redirect "#{shared_dir}/log/puma.stdout.log", "#{shared_dir}/log/puma.stderr.log", true
# Set master PID and state locations
pidfile "#{shared_dir}/pids/puma.pid"
state_path "#{shared_dir}/pids/puma.state"
activate_control_app
on_worker_boot do
require "active_record"
ActiveRecord::Base.connection.disconnect! rescue ActiveRecord::ConnectionNotEstablished
ActiveRecord::Base.establish_connection(YAML.load_file("#{app_dir}/config/database.yml")[rails_env])
end
Then restart Nginx and the systemctl daemon.
sudo systemctl daemon-reload
sudo service nginx restart
Let's install the package. After we install it, we are going to tell systemd to manage it.
sudo apt-get update && sudo apt-get install redis-server
Enable the service
sudo systemctl enable redis-server.service
Now open the service file it creates and change supervised to be systemd.
sudo vim /etc/redis/redis.conf
# /etc/redis/redis.conf
supervised systemd
Exit and then restart redis.
sudo systemctl reload redis.service
sudo systemctl status redis