Shiny Server Options

July 4, 2020

Creating R Shiny apps has been useful in class demonstration, project assignments, and customized tests. However, to host and share these apps, a server is needed. I have tried three options so far: Shinyapps.io, Amazon Web Service (AWS), and Linode.


Shinyapps.io

This is the easiest way. In RStudio IDE, the Publish button deploys the Shiny apps to RStudio’s web host for shiny apps: https://www.shinyapps.io/. Here is the full tutorial from RStudio.

A free plan on shinyapps.io has limits on run-time and number of deployed apps. Still, it is a good way for getting started with Shiny apps. Plans on shinyapps.io are pricy comparing with AWS and Linode.


Amazon Web Service (AWS)

We are on our own to set up a shiny server (and more) in the AWS Elastic Compute Cloud (EC2). Charles Bordet wrote a wonderful step-by-step installation guide, The Ultimate Guide to Deploying a Shiny App on AWS.

The general purpose (burstable) instance families include T2 (July 2014), T3 (August 2018) and T3a (April 2019), each comes with a variety of capacity (nano / micro / small / medium / large / xlarge) in memory sizes and vCPUs. A new generation is 10% cheaper than the previous generation.

Amazon generously offers a one-year Free Tier plan (t2.micro / 1GB Memory / 1 vCPU / 8 GB Storage) Afterwards, users can choose from a handful of pricing plans, such as On-Demand and 1-year or 3-year Saving Plans. Users can estimate the cost. Below is a comparison of t3a.micro 3-year Saving Plan with Linode’s entry-level plan.

Plan Memory vCPUs Storage Monthly Price
t3a.micro 1 GB 2 20 GB $4.99
Linode 1 GB 1 25 GB $5.00

I started with 8 GB elastic block storage (EBS) and went up to 20 GB. Resizing requires two steps:

1. Modifying an EBS volume using Elastic Volumes. This can be done in the AWS console.

2. Extending a Linux file system after resizing a volume. This needs to be done in a terminal.

ssh -i key_name.pem ubuntu@ec2-xxx-xxx-xxx-xxx.us-east-2.compute.amazonaws.com
df -h
lsblk
sudo growpart /dev/xvda 1
sudo resize2fs /dev/xvda1


Linode

Since I have learned how to set up a Shiny server on AWS, getting started in Linodes seems easier and faster. Here is Linode’s guide: Getting Started with Linode. By the way, besides using the terminal to type commands, Linode provides a web-based shell, the Linode Shell (Lish) in the account interface.

Below are my notes for quick future reference.

Secure Your Server. Login as root on Linode to add a limited user account.

ssh root@xxx.xxx.xxx.xxx
adduser example_user
adduser example_user sudo

Enable SSH key pairs. Login to Linode example_user, create a .ssh folder and grant read / write / execute permission.

mkdir -p ~/.ssh && sudo chmod -R 700 ~/.ssh/

On Mac OS terminal, check existing keys, generate a key pair, and upload the public key to Linode.

ls ~/.ssh/id_rsa*
ssh-keygen -b 4096
scp ~/.ssh/id_rsa.pub example_user@xxx.xxx.xxx.xxx:~/.ssh/authorized_keys

Back to Linode. Add CRAN repo and install R 3.6.

sudo apt-key adv –keyserver keyserver.ubuntu.com –recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
sudo add-apt-repository ‘deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/’
sudo apt update
sudo apt install r-base

Install Shiny server. First install the Shiny package in R, then use GDebi to install shiny-server.

sudo su - \
-c “R -e \”install.packages(‘shiny’, repos=‘https://cran.rstudio.com/’)\""
sudo apt-get install gdebi-core
wget https://download3.rstudio.org/ubuntu-14.04/x86_64/shiny-server-1.5.14.948-amd64.deb
sudo gdebi shiny-server-1.5.14.948-amd64.deb
sudo systemctl status shiny-server.service

Hide directory by setting directory_index off; in shiny-server.conf.

sudo nano /etc/shiny-server/shiny-server.conf

# Instruct Shiny Server to run applications as the user "shiny"
run_as shiny;

# Define a server that listens on port 3838
server {
  listen 3838;

  # Define a location at the base URL
  location / {

  # Host the directory of Shiny Apps stored in this directory
  site_dir /srv/shiny-server;

  # Log all Shiny output to files in this directory
  log_dir /var/log/shiny-server;

  # When a user visits the base URL rather than a particular application,
  # an index of the applications available in this directory will be shown.
  directory_index off;
  }
}
Reload Shiny server after making changes in shiny-server.conf.

sudo systemctl reload shiny-server

Install more R packages.

sudo su - \
-c “R -e \”install.packages(‘package_name’, repos=‘https://cran.rstudio.com/’)\""

Upload shiny apps from Github and create shortcut links in the /srv/shiny-server folder. Error messages can be found in the folder /var/log/shiny-server.

git clone https://github.com/github_user_name/app_name.git
cd /srv/shiny-server
sudo ln -s ~/app_name

Alternatively, we can transfer files between local drive and Linode in Mac OS Terminal (not in Linode!).

scp local_path_to_file example_user@xxx.xxx.xxx.xxx:/home/example_user
scp example_user@xxx.xxx.xxx.xxx:/home/example_user local_path_to_file

Use NGINX to set up reverse proxy so that the app can be access by example.com/app_name instead of xxx.xxx.xxx.xxx:3838/app_name. (80 is the default port number for the website. The configuration redirects example.com to Shiny’s port 3838)

sudo apt install nginx
sudo nano /etc/nginx/nginx.conf

http {
        # Basic Settings
        
        # Hide version number
        server_tokens off;

        map $http_upgrade $connection_upgrade {
            default upgrade;
            ''      close;
        }
}

sudo nano /etc/nginx/sites-available/shiny.conf

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

    server_name example.com;
    location / {
        proxy_pass http://localhost:3838;
        proxy_redirect http://localhost:3838/ $scheme://$host/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 20d;
        proxy_buffering off;
    }
}

cd ../sites-enabled
sudo ln -s ../sites-available/shiny.conf .

Check NGINX configuration and restart NGINX server.

sudo nginx -t
sudo systemctl restart nginx

Secure HTTP Traffic with Certbot

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install python-certbot-nginx
sudo certbot --nginx

Update the shiny.conf file to enforce HTTPS connections only, TLS Deployment Best Practices. Make sure the lines and block commented by # managed by Certbot are already in place.

sudo nano /etc/nginx/sites-available/shiny.conf

server {
    listen 443 ssl http2 ipv6only=on; # managed by Certbot
    listen [::]:443 ssl http2; # managed by Certbot

    # HSTS max-age=31536000 seconds means one year
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    server_name example.com;
    location / {
        proxy_pass http://localhost:3838;
        proxy_redirect http://localhost:3838/ $scheme://$host/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 20d;
        proxy_buffering off;
    }
}

server {
    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    listen [::]:80;

    server_name example.com;
    return 404; # managed by Certbot
}
Again, check NGINX configuration and reload it.

sudo nginx -t
nginx -s reload

Optional firewall configuration.

sudo apt install ufw
sudo systemctl start ufw && sudo systemctl enable ufw
sudo ufw allow http
sudo ufw allow https
sudo ufw allow 22/tcp
sudo ufw enable

Optional RStudio server installation. NGINX reverse proxy is needed as well.