Running Shiny Server in Docker
We looked at best practices for R with Docker where we compared four commonly used parent images. It was common in these images that these all contained a "local" Shiny app using shiny::runApp()
. A special breed of dockerized Shiny apps is when the Shiny Server is running inside the Docker container. In this post, we'll explore this "dockerized Shiny Server" setup and discuss the pros and cons.
The files from this post can be found in the analythium/covidapp-shiny GitHub repository, inside the 99-images
folder.
The Dockerfile
The parent image of the Dockerfile
is rocker/shiny
. This image is part of the Rocker project and is based on the versioned Ubuntu line of R images. It contains a versioned R installation, Shiny Server Open Source. It already has the shiny
user defined and port 3838 exposed.
On top of the parent image, you can follow the same pattern that we used for other base images: install system dependencies, install R packages, copy files for the Shiny app. The Shiny app goes inside the /srv/shiny-server/
folder where you can have more than one shiny app in different folders. You may also copy HTML files to provide navigation for your users.
We'll copy the files for the COVID-19 app, change the user to shiny
(the EXPOSE
directive is not needed but I left it there as a reminder). Finally, we define the command as /usr/bin/shiny-server
to start the Shiny Server:
FROM rocker/shiny:4.0.5
# Install system requirements for index.R as needed
RUN apt-get update && apt-get install -y \
--no-install-recommends \
git-core \
libssl-dev \
libcurl4-gnutls-dev \
curl \
libsodium-dev \
libxml2-dev \
libicu-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
ENV _R_SHLIB_STRIP_=true
COPY Rprofile.site /etc/R
RUN install2.r --error --skipinstalled \
shiny \
forecast \
jsonlite \
ggplot2 \
htmltools \
plotly
COPY ./app/* /srv/shiny-server/
USER shiny
EXPOSE 3838
CMD ["/usr/bin/shiny-server"]
The Docker image
I used the Docker BuildKit as explained before, then tested the image. Here is the script for your to replicate:
export IMAGE="analythium/covidapp-shiny:shiny"
export FILE="Dockerfile.shiny"
DOCKER_BUILDKIT=1 docker build --no-cache -f $FILE -t $IMAGE .
docker run -p 8080:3838 $IMAGE
Here is how build times and image sizes compare across the five images:
Parent image | Parent size (GB) | Final size (GB) | Bild time (min) |
---|---|---|---|
rhub/r-minimal |
0.035 | 0.222 | 27.0 |
rocker/r-base |
0.761 | 1.050 | 2.9 |
rocker/r-ubuntu |
0.673 | 1.220 | 3.1 |
rstudio/r-base |
0.894 | 1.380 | 3.1 |
rocker/shiny |
1.380 | 1.610 | 2.3 |
Build time was comparably small for all the other non-Alpine images, however, the size of the parent and the final images were the largest among the 5 different options.
Here are some thoughts to consider when using the rocker/shiny
parent image to host a containerized Shiny applications.
You can run multiple Shiny apps over the same port exposed by Docker. The Shiny Server configuration allows you to disable the websocket protocol, this way you can host your apps in Container-as-a-Service environments, like Google Cloud Run, etc. This is achieved by using SockJS under the hood that can emulate websocket connections.
However, the image size is the largest among the comparable options. If the image includes multiple apps, those apps are not isolated, and managing their dependencies might become problematic over time.
An alternative use case for the rocker/shiny
image is to host Shiny apps that are not copied into the image. This is done by mounting the app folder as a volume to the container (that is a live instance of the image) as explained on the Docker Hub page.
The next command adds two volumes, one with the apps and one for the Shiny Server logs in a -v host:container
fashion. The directories on the host will be created if those don't already exist.
docker run -d -p 8080:3838 \
-v /srv/shinyapps/:/srv/shiny-server/ \
-v /srv/shinylog/:/var/log/shiny-server/ \
rocker/shiny
This setup can be especially useful for hosting Shiny Apps in a Windows environment. You will need to add dependencies using the RUN
directive.
Summary
The dockerized Shiny Server is a heavyweight option for hosting a single app, but it plays an important role in the Shiny hosting landscape. It allows you to run Shiny apps in unusual situations, like on Windows, or hosted container platforms.
Further reading
- A detailed blog post by Mark Sellors
- Scaling Shiny in Google Cloud by Mark Edmondson
- Mounting directories on Windows