File Transfer Based Publishing for Shiny Apps
Tags: shiny, rstats
Published: 2021-05-27 16:00:49

*By: [Peter Solymos](https://hosting.analythium.io/author/peter/)*

Shiny Server is a free and open-source option for self hosting Shiny
apps and it is one of the 3 options listed on the [Shiny
website](https://shiny.rstudio.com/deploy/). In a [previous
post](https://hosting.analythium.io/securing-shiny-server-with-caddy/)
you saw how to secure Shiny Server with a custom domain. Here you will
lear how to add and update Shiny apps to your server.

Follow the instructions from the previous post or spin up a brand new
virtual machine on DigitalOcean using the [RStudio 1-click
app](https://marketplace.digitalocean.com/apps/rstudio) in minutes.

The `index.html` file for the Shiny Server's landing page and the
`hello` and `rmd` apps are in the `/srv/shiny-server/` folder:

    $ ls /srv/shiny-server/
    index.html  sample-apps

    $ ls /srv/shiny-server/sample-apps/hello
    server.R  ui.R

    $ ls /srv/shiny-server/sample-apps/rmd
    index.Rmd

These directories map to the server path as:

-   `http://$HOST` is the landing page (`index.html`),
-   `http://$HOST/sample-apps/hello/` is the `hello` app,
-   `http://$HOST/sample-apps/rmd/` is the `rmd` app.

`$HOST` is the custom domain (e.g. `yourdomain.com` ) your Shiny Server
server is using. You can see that the folder structure inside
`/srv/shiny-server/` directly translates to the server paths. How do you
add more apps to the server? Just copy the Shiny apps directly into
folders within the `/srv/shiny-server/` directory. Here are three
options for doing it.

<figure>
<img src="https://hosting.analythium.io/content/images/2021/05/shinyserver.jpg" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/05/shinyserver.jpg 600w, https://hosting.analythium.io/content/images/size/w1000/2021/05/shinyserver.jpg 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/05/shinyserver.jpg 1600w, https://hosting.analythium.io/content/images/size/w2400/2021/05/shinyserver.jpg 2400w" width="2000" height="1124" alt="" /><figcaption>File based deployment of Shiny apps to Shiny Server</figcaption>
</figure>

Edit text files on the server
-----------------------------

Let's add an app called `histogram` to the `http://$HOST/histogram/`
path:

    mkdir /srv/shiny-server/histogram
    cd /srv/shiny-server/histogram
    touch app.R
    nano app.R

Copy-paste the Shiny app from below into `app.R` that you just opened
with `nano` (Ctrl+O to save, Ctrl+X to exit `nano`):

    library(shiny)
    ui = fluidPage(
        mainPanel(
            sliderInput("obs",
                "Number of observations",
                min = 1,
                max = 5000,
                value = 100),
            plotOutput("distPlot")
        )
    )
    server = function(input, output) {
        output$distPlot = renderPlot({
            dist = rnorm(input$obs)
            hist(dist,
                col="purple",
                xlab="Random values")
        })
    }
    shinyApp(ui = ui, server = server)

Now if you visit `http://$HOST/histogram/` you'll see a range slider
controlling sample size and a purple coloured histogram of a Normal
distribution that must be familiar from a [previous
post](https://hosting.analythium.io/the-anatomy-of-a-shiny-application/).

### Secure copy the files

You can use `scp` to copy local files to the server. Let's create a
small Shiny app on your local machine. Use the same code as for the
purple histogram, but change some of the settings:

    mkdir pink
    touch pink/app.R
    nano pink/app.R

Copy this code into the file `pink/app.R`:

    library(shiny)
    ui = fluidPage(
        mainPanel(
            sliderInput("obs",
                "Number of observations",
                min = 1,
                max = 1000,
                value = 100),
            plotOutput("distPlot")
        )
    )
    server = function(input, output) {
        output$distPlot = renderPlot({
            dist = runif(input$obs)
            hist(dist,
                col="pink",
                xlab="Random values")
        })
    }
    shinyApp(ui = ui, server = server)

The following script copies the local `pink` directory over to the
`pinkhist` directory of the Shiny Server (this use of `scp` assumes you
access the server using your `ssh` key pair, otherwise you'll be
prompted to provide your username/password):

    export APPDIR="pink"
    export SHINYDIR="pinkhist"

    scp -r $APPDIR root@$HOST:/srv/shiny-server/$SHINYDIR

Visiting the `http://$HOST/pinkhist/` address should reveal the new app:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/05/Screen-Shot-2021-05-18-at-9.44.22-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/05/Screen-Shot-2021-05-18-at-9.44.22-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/05/Screen-Shot-2021-05-18-at-9.44.22-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/05/Screen-Shot-2021-05-18-at-9.44.22-PM.png 1600w, https://hosting.analythium.io/content/images/2021/05/Screen-Shot-2021-05-18-at-9.44.22-PM.png 1740w" width="1740" height="1416" alt="" />
</figure>

Note that the `scp` protocol uses port 22 similarly to `ssh` and `sftp`.
If you are using the non-secure FTP protocol to copy the files, make
sure port 21 of your server is open for incoming traffic.

### Git based deployment

Set up `git` so you can work with your public and private repositories
(`git` is already installed, this is a one-time setup for each user):

    git config --global user.email "you@example.com"
    git config --global user.name "Your Name"

Change to your home directory with `cd ~` and run the following script.
This will clone the GitHub repository specified in the environment
variables, or pull changes if the directory already exists; then makes a
directory for Shiny Server if that directory is not already there, then
copies the app files (`$APPDIR` can be empty):

    #!/bin/bash

    export GITHOST="https://github.com"
    export GITUSER="analythium"
    export GITREPO="covidapp-shiny"
    export APPDIR="02-shiny-app/app"
    export SHINYDIR="covidapp"

    # clone or pull repo
    if [[ ! -e $GITREPO ]]; then
        git clone $GITHOST/$GITUSER/$GITREPO.git
    else
        cd $GITREPO
        git pull
        cd ..
    fi

    # make dir if not already there
    mkdir -p /srv/shiny-server/$SHINYDIR

    # copy repo contents to Shiny Server
    cp -rf $GITREPO/$APPDIR/* /srv/shiny-server/$SHINYDIR

You can modify this script for other repositories and save in a file,
e.g. `update_covid.sh` . Then you can run `bash update_covid.sh` every
time you need the app to be updated. It is also possible to set up a
[cron job](https://crontab.guru/every-day-8am) to update the app daily
or tie the script to a webhook event triggered by a successful GitHub
action.

But before you do any of that, check if the app is running fine at
`http://$HOST/covidapp/`. If all went wrong, you should see this
message:

<figure>
<img src="https://hosting.analythium.io/content/images/2021/05/Screen-Shot-2021-05-18-at-8.50.59-PM.png" class="kg-image" sizes="(min-width: 720px) 720px" srcset="https://hosting.analythium.io/content/images/size/w600/2021/05/Screen-Shot-2021-05-18-at-8.50.59-PM.png 600w, https://hosting.analythium.io/content/images/size/w1000/2021/05/Screen-Shot-2021-05-18-at-8.50.59-PM.png 1000w, https://hosting.analythium.io/content/images/size/w1600/2021/05/Screen-Shot-2021-05-18-at-8.50.59-PM.png 1600w, https://hosting.analythium.io/content/images/2021/05/Screen-Shot-2021-05-18-at-8.50.59-PM.png 1716w" width="1716" height="886" alt="" />
</figure>

This happened because the Shiny Server setup only included the most
basic R packages and we missed the forecast package required by the
COVID-19 app. Let's remedy this:

    R -q -e "install.packages('forecast')"

If you refresh the page now the app should work fine.

This immediately highlights one of the shortcomings of Shiny Server when
it comes to continuous integration and delivery (CICD). You need to be
extra careful when managing packages, R and package versions, etc. when
you have multiple apps with possibly conflicting dependencies.
[Dockerizing Shiny apps](https://hosting.analythium.io/dockerized-shiny-apps-with-dependencies/) is one solution to isolate your applications.

License
-------

R and the shiny R package are both licensed under the [GNU General
Public License](http://www.gnu.org/licenses/gpl-3.0.txt) that permits
making a modified version and letting the public access it on a server
without ever releasing its source code to the public. However, it is
important to note that the open source version of Shiny Server is
licensed under the [GNU Affero General Public
License](http://www.gnu.org/licenses/agpl-3.0.html) (AGPLv3).

AGPL closes the [application service
provider](https://en.wikipedia.org/wiki/Application_service_provider)
(ASP) loophole and has a controversial reputation in tech circles, see
for example [Google's
policy](https://opensource.google/docs/using/agpl-policy/) on banning
the license. We are not providing legal advice on this matter here,
merely stating what the license says:

> The GNU General Public License permits making a modified version and
> letting the public access it on a server without ever releasing its
> source code to the public. The GNU Affero General Public License is
> designed specifically to ensure that, in such cases, the modified
> source code becomes available to the community. It requires the
> operator of a network server to provide the source code of the
> modified version running there to the users of that server. Therefore,
> public use of a modified version, on a publicly accessible server,
> gives the public access to the source code of the modified version. –
> [AGPLv3 Preamble](http://www.gnu.org/licenses/agpl-3.0.html)

The interpretation of modified source code and public accessibility on a
server leaves a lot of room for interpretation (see some discussions
[here](https://community.rstudio.com/search?q=shiny%20server%20agpl))
which might or might not bother you depending on your intentions and
corporate environment. RStudio offers paid exception to the AGPL license
in the form of [RStudio
Connect](https://community.rstudio.com/t/agpl-seems-to-prevent-use-of-shiny-applications-in-a-proprietary-environment/17795/2)
– this model called dual-licensing (read about it
[here](https://moderndata.plotly.com/why-dash-uses-the-mit-license/)).

Summary
-------

The open-source Shiny Server is an easy way to self-host Shiny apps on a
low-cost virtual machine. You can set up the server yourself or use an
existing image and deploy apps in seconds. Some care must be taken with
dependencies, so it is good practice to use `git` or do periodic backups
of the server to revert any breaking changes.

If you have followed this tutorial by spinning up your server that you
don't need any more, don't forget to destroy the server after you are
finished to avoid surprise billing.

Further reading
---------------

-   The official [Shiny Server
    docs](https://docs.rstudio.com/shiny-server/)
-   [Useful tips for logging and user
    permissions](https://deanattali.com/2015/05/09/setup-rstudio-shiny-server-digital-ocean/)
    from Dean Attali
-   [Multiple users managing their
    apps](https://support.rstudio.com/hc/en-us/articles/218541988-Shiny-Server-Quick-Start-Let-users-manage-their-own-applications)
    on the same server