8451 380 X 240 Shutterstock 1517113610 M

Dockerizing Shiny Apps

By: David Schappler, Data Scientist

Within the data science world, a commonly cited struggle is getting the results of an analysis embedded in an already existing business process – in other words, having the stakeholder use the data.

Often, it is easier to embed science into a process if the stakeholders have input into the process rather than simply working from flat results. Shiny Apps help Data Scientists to provide flexible sciences that allow for stakeholder input.

What is Docker?

Docker is an open source tool that allows Data Scientists to package applications with all dependencies, packages, and data into a self-sustainable container that can then be deployed to users as needed.

Why Dockerize a Shiny App?

One obvious question exists – if I have access to Shiny Server, why would I Dockerize my Shiny App? Well, you might not! Dockerizing an app is an alternative solution for those who do not have a dedicated Shiny Server, though still wish to deploy apps to stakeholders.

In addition to this, Dockerizing an app allows for the app to be reproducible by anybody. Because the container has all the package, data, and code dependencies necessary to run the app, it should be easily transferrable in the case of work handoff or collaborative coding.

How to Dockerize a Shiny App

The Dockerfile

The key to creating a Docker container is the Dockerfile. The Dockerfile is a set of instructions that tells Docker how to create the container. Below is an example Dockerfile that will Dockerize a Shiny App. The prerequisites for this Dockerfile example are:

  1. The app, and any relevant images, data, etc. are located in a folder named app/

  2. The app/ folder must contain a server.R and ui.R file that run the Shiny app

Example Dockerfile

FROM rocker/shiny:3.5.1 RUN apt-get -y install libcurl4-gnutls-dev libxml2-dev libssl-dev; \ rm -r /srv/shiny-server; \ mkdir -p /var/lib/shiny-server/bookmarks/shiny; \ sed -i 's/3838/3838' /etc/shiny-server/shiny-server.conf RUN R -e "install.packages(pkgs=c('shiny','tidyverse','shinydashboard'), repos='https://cran.rstudio.com/')" COPY /app /srv/shiny-server/ RUN chmod -R +rx /srv/shiny-server/ USER shiny EXPOSE 3838 CMD ["/usr/bin/shiny-server.sh"]

Now, let’s break down this Dockerfile to look at how it works.

FROM rocker/shiny:3.5.1

This part tells Docker what source image to use. We are going to be using the shiny source image from rocker. You can check the documentation for this image and see the full code here.

RUN apt-get -y install libcurl4-gnutls-dev libxml2-dev libssl-dev; \ rm -r /srv/shiny-server; \ mkdir -p /var/lib/shiny-server/bookmarks/shiny; \ sed -i 's/3838/3838' /etc/shiny-server/shiny-server.conf

This chunk will install some linux utilities that are necessary to run shiny server. It will also change some of the files from the rocker version of shiny so that we can deploy our app. Specifically, take a look at this line:

sed -i 's/3838/3838' /etc/shiny-server/shiny-server.conf

Here, we are modifying the default shiny-server.conf file so that we open traffic to any IP (the 0 .0.0.0), and we are keeping the port of our app at the default of 3838. If we wanted to change port numbers, we would change only the second 3838 to whatever port number we wanted. For example, to change the port number to 1738, we would change the line to look like this:

sed -i 's/3838/1738' /etc/shiny-server/shiny-server.conf

And now we would be deploying the app to port 1738 instead of 3838.

RUN R -e "install.packages(pkgs=c('shiny','tidyverse','shinydashboard'), repos='https://cran.rstudio.com/')"

This line installs whatever R packages we need into the container. Simply add packages within the pkgs=c() argument that you want installed. This runs the command using R, so you can provide any additional function arguments that install.packages() accepts. For full documentation, see here.

If you need an older version of a package, or simply want your container to always use a specific version, you can pass the full URL of the package version you want rather than the name of the package. For example, if we want ggplot2 to be frozen at version 0.9.1, we could add this line to our Dockerfile:

RUN R -e "install.packages(pkgs='http://cran.r-project.org/src/contrib/Archive/ggplot2/ggplot2_0.9.1.tar.gz',repos=NULL,type='source')"

This will install the specific version of ggplot2 into our container, and make future-proofing the Shiny App easier.

COPY /app /srv/shiny-server/ RUN chmod -R +rx /srv/shiny-server/

This tells the Dockerfile to copy our /app folder into the /srv/shiny-server/ folder in our container. The end pathway will be /srv/shiny-server/app. We then chmod our folder so that the files can be run by the container.

USER shiny EXPOSE 3838 CMD ["/usr/bin/shiny-server.sh"]

The last few lines here tell the container that we are declaring a USER for the container, and that user is shiny. We then expose the app to our port number so that we can access the Shiny App via a URL. Lastly, we run the shiny-server shell script (provided by the rocker/shiny:3.5.1 image) to kick off shiny server, running our app in the __/srv/shiny-server/app__ folder.

Deploying the App

Once your Dockerfile is complete, simply create a folder that has the Dockerfile and the app/ folder in it.

Then, simply navigate to the folder containing the Dockerfile and run the command docker build -t <NAME> <DOCKERFILE_PATH>

<NAME>, in this case, is the name you want to give your Docker image. <DOCKERFILE_PATH> is the file path to the Dockerfile (if you are in the folder already, just enter .)

For example:

cd /path/to/dockerfile/ docker build -t example_shiny_docker .

This will begin the creation of the Docker image, using your Dockerfile as instructions.

Once the image is created, you can see deploy the image to the given port by using docker run -p <PORT>:3838 <NAME>

<NAME> must match the name of the Docker image you created before, and <PORT> should match the port # in your Dockerfile.

For example:

docker run -p 3838:3838 example_shiny_docker

Once this is entered, you are done! The app will now be accessible hosted from your server at the port # specified.

David Schappler, Data Scientist

We’re leading a data revolution in the retail business, and we’re looking for partners who are ready for a deeper, more personal approach to customer engagement.

Let’s connect