Docker vs. Podman vs. Singularity: Which Containerization Platform is Best for R Shiny?
When it comes to deploying R Shiny applications, especially in the life sciences, the containerization approach is hard to beat. You can easily deploy one or more services, such as individual Shiny apps, or Shiny apps that also require a database, a custom API, or something else entirely. Containerization has made deployment a breeze because if the code works on your machine, it will work on others as well.
The question is - <b>Which containerization platform is best for you?</b> Docker is probably the most popular, but there are other players in this niche. Today, we'll compare Docker vs. Podman vs. Singularity with the aim of demystifying which containerization platform is the best in the context of R Shiny.
But first, let's go over the basics of containerization. Feel free to skip this section if you're already familiar with the concepts.
<blockquote>Is the concept of reactivity in R Shiny giving you a hard time? <a href="https://appsilon.com/r-shiny-reactivity/" target="_blank" rel="noopener">Master the basics with our latest piece on R Shiny reactivity</a>.</blockquote>
Table of contents:
<ul><li><a href="#what-is-containerization">What is Containerization and Why Should You Care</a></li><li><a href="#key-differences">Docker vs. Podman vs. Singularity - What are the Differences?</a></li><li><a href="#r-shiny">Docker vs. Podman vs. Singularity - Which is Best for R Shiny?</a></li><li><a href="#summary">Summing up Docker vs. Podman vs. Singularity</a></li></ul>
<hr />
<h2 id="what-is-containerization">What is Containerization and Why Should You Care</h2>
You can think of containerization as a <b>method of packaging software in a way that runs consistently and uniformly on any infrastructure</b>. You've probably been in a situation when the code works on your machine, but there's some dependency missing or a version mismatch happening on the client machine. This is a problem since you won't give your laptop to the client.
Containerization solves this issue by simplifying the deployment of applications across different environments, from your machine to your colleague's machine, to internal servers and cloud environments.
There are many reasons why you should bring containerization to your R and R Shiny projects, such as:
<ul><li><b>Scalability</b>: You can use system resources more efficiently because containers are lightweight, quick to deploy, and also quick to scale.</li><li><b>Agility</b>: Because of their isolated nature, updates, and troubleshooting performed on one container won't impact any applications running in different containers.</li><li><b>Portability</b>: You can run the same application on different operating systems without the need to rewrite portions of code or change the application's configuration.</li><li><b>Fault tolerance</b>: Containerized applications are resilient, meaning that a sudden failure in one container won't have any impact on others, ensuring higher availability.</li><li><b>Cloud migrations</b>: Your company decided to migrate to the cloud but has a lot of legacy applications. You can encapsulate the legacy code in a container a move the applications to the cloud almost effortlessly.</li></ul>
And many, many others. Containers are the way in which the industry is moving and are here to stay. As a developer, you should do your best to learn the basics at a bare minimum, but a more proficient understanding is always welcome.
<b>But which containerization platform should you use as a R Shiny developer?</b> Let's go over a couple of common ones next.
<h2 id="key-differences">Docker vs. Podman vs. Singularity - What are the Differences?</h2>
In this section, we'll dive deep into the characteristics and key differences between <a href="https://www.docker.com/" target="_blank" rel="noopener noreferrer">Docker</a>, <a href="https://podman.io/" target="_blank" rel="noopener noreferrer">Podman</a>, and <a href="https://docs.sylabs.io/guides/3.5/user-guide/introduction.html" target="_blank" rel="noopener noreferrer">Singularity</a>.
At the time of writing this article (November 2023), Docker is the most recognized name in the realm of container platforms but is far from being the only one in the market. Each platform you'll see listed below has its own set of advantages and limitations, and understanding the differences will help you select the optimal tool for your needs, particularly when deploying R Shiny applications.
<h3>Docker</h3>
Established in 2013, Docker is somewhat of a veteran in the containerization space and is widely known for its superb developer experience. It's often used as a synonym for containerization since it has massively popularized the technology.
<img class="size-full wp-image-21601" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae24c5eb5b7ebba052a8_14c7373b_1.webp" alt="Image 1 - Docker home page" width="2009" height="1434" /> Image 1 - Docker home page
Docker offers a comprehensive feature set for operating containers in development and production environments. The feature set includes easy-to-use CLI commands, a vast collection of public images on Docker Hub, and the ability to build and share container images with a global community. Advanced features, such as orchestration with <a href="https://docs.docker.com/engine/swarm/" target="_blank" rel="noopener noreferrer">Docker Swarm</a> or <a href="https://kubernetes.io/" target="_blank" rel="noopener noreferrer">Kubernetes</a>, make it a go-to tool for companies looking to productionize their applications. Docker also has extensive documentation that covers a wide array of topics, providing learning resources for newcomers and advanced guides for experienced users.
Regarding pricing, <b>Docker is free for personal use</b>, but a <a href="https://www.docker.com/pricing/faq/" target="_blank" rel="noopener noreferrer">paid subscription</a> is required for organizations with more than 250 employees or more than $10 million in annual revenue. The business tier subscription starts at $120 for the first 5 users and $24 per additional user when charged monthly on an annual basis.
So, if you're just starting out - yes, you can use Docker free of charge. If you own a company that has more than 250 employees, it's unlikely Docker will put a large dent in your budget.
<strong>Life Sciences:</strong> Docker offers an exceptional array of tools and support to the life science sector, positioning it as a leader considering the stringent regulations that exist in the field. A prime example is <a href="https://docs.docker.com/scout/" target="_blank" rel="noopener noreferrer">Docker Scout</a>, which is adept at detecting security vulnerabilities in Containers. Its extensive library of public images includes numerous tools commonly used in biological data analysis. However, the lack of rootless container support may raise security concerns when dealing with sensitive biological data.
The shift toward licensing open-source projects is prompting users to explore alternatives, as seen in projects like <a href="https://www.r-consortium.org/announcement/2023/09/11/first-publicly-available-r-based-submission-package-submitted-to-fda-pilot-3" target="_blank" rel="noopener noreferrer">R Consortium’s FDA pilot</a>.
<h3>Podman</h3>
This containerization tool emerged as an alternative to Docker and puts focus on security and simplicity. It's basically a daemonless container engine developed by Red Hat. Podman operates similarly to Docker as both comply with the OCI specification for containers (<a href="https://opencontainers.org/" target="_blank" rel="noopener noreferrer">Open Container Initiative</a>), which makes the transition period short and smooth.
<img class="size-full wp-image-21603" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae240f03f2fe351599ae_a35623e0_2.webp" alt="Image 2 - Podman home page" width="2009" height="1434" /> Image 2 - Podman home page
Podman's unique selling point is its rootless mode, which enables users to run containers without root privileges. It offers a command-line interface that's almost identical to Docker's and integrates with <a href="https://buildah.io/" target="_blank" rel="noopener noreferrer">Buildah</a> and <a href="https://github.com/containers/skopeo" target="_blank" rel="noopener noreferrer">Skopeo</a>, allowing for advanced image building and management. The absence of a daemon improves security but requires integration with system services like systemd for container management.
Regarding pricing, you'll be pleased to know that <b>Podman is open-source and free to use</b>. It was developed by Red Hat and is included in many Linux distributions. There are no direct costs associated with the tool, which makes it an interesting platform for companies on a tighter budget.
<strong>Life Sciences:</strong> Podman offers enhanced security, which is critical for handling sensitive data. Its modular design, relying on other tools for image builds, can be seen as both a flexibility and complexity factor. In essence, for life science environments that prioritize security and open–source ethos, Podman emerges as a strong candidate.
<h3>Singularity</h3>
Released in 2015, Singularity aims to cover a containerization market for high-performance computing tasks. It's particularly popular in scientific computing and allows users to execute containers as non-root users, ensuring a high level of security for sensitive computational workloads.
<img class="size-full wp-image-21605" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae25c83b17271013c04a_e5ef3464_3.webp" alt="Image 3 - Singularity home page" width="2009" height="1434" /> Image 3 - Singularity home page
Singularity supports a range of advanced features tailored for compute-intensive tasks, such as seamless access to GPUs, shared file systems, and compatibility with common high-performance computing (HPC) workflows. The platform is designed to handle complex applications and data-intensive workloads that are typical in research environments.
When it comes to pricing, <b>SingularityCE (Community Edition) is open-source and free</b>. For companies and organizations that require professional support and additional features, commercial versions are available. However, their pricing is not publicly listed and is typically provided upon request for a quote based on your specific needs.
<strong>Life Sciences:</strong> Singularity is gaining traction in the life sciences and caters specifically to high-performance computing needs. Its design for running containers as non-root users provides a secure environment for processing sensitive and large-scale biological datasets. Although less known than Docker and Podman, Singularity’s focus on scientific and research computing aligns well with the specialised requirements of life science applications.
<h2 id="r-shiny">Docker vs. Podman vs. Singularity - Which is Best for R Shiny?</h2>
In the previous section, you've seen that each containerization platform offers something specific and unique. For R Shiny applications, Docker's widespread adoption and ease of use make it a strong contender, while Podman's security features could be particularly beneficial for multi-user environments. Singularity would be highly relevant for R Shiny applications that are used in research and scientific computations due to its focus on high-performance computing.
We'll now go over a simple R Shiny application to see firsthand what it takes to containerize it with all three tools.
<h3>Writing a Shiny Application</h3>
The Shiny application we'll build today will feel familiar if you've been following along with our Docker series.
In a nutshell, the app displays data from the <code>gapminder</code> dataset by allowing the user to select a continent. Upon change, the average life expectancy and average GDP charts are updated.
Just note how we're explicitly specifying host and port values on which the app will run. This will make the containerization process a bit smoother:
<pre><code class="language-r">library(shiny)
library(dplyr)
library(ggplot2)
library(gapminder)
<br># Specify the application port
options(shiny.host = "0.0.0.0")
options(shiny.port = 8180)
<br>
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
tags$h4("Gapminder Dashboard"),
tags$hr(),
selectInput(inputId = "inContinent", label = "Continent", choices = unique(gapminder$continent), selected = "Europe")
),
mainPanel(
plotOutput(outputId = "outChartLifeExp"),
plotOutput(outputId = "outChartGDP")
)
)
)
<br>server <- function(input, output, session) {
# Filter data and store as reactive value
data <- reactive({
gapminder %>%
filter(continent == input$inContinent) %>%
group_by(year) %>%
summarise(
AvgLifeExp = round(mean(lifeExp)),
AvgGdpPercap = round(mean(gdpPercap), digits = 2)
)
})
# Common properties for charts
chart_theme <- ggplot2::theme(
plot.title = element_text(hjust = 0.5, size = 20, face = "bold"),
axis.title.x = element_text(size = 15),
axis.title.y = element_text(size = 15),
axis.text.x = element_text(size = 12),
axis.text.y = element_text(size = 12)
)
# Render Life Exp chart
output$outChartLifeExp <- renderPlot({
ggplot(data(), aes(x = year, y = AvgLifeExp)) +
geom_col(fill = "#0099f9") +
geom_text(aes(label = AvgLifeExp), vjust = 2, size = 6, color = "#ffffff") +
labs(title = paste("Average life expectancy in", input$inContinent)) +
theme_classic() +
chart_theme
})
# Render GDP chart
output$outChartGDP <- renderPlot({
ggplot(data(), aes(x = year, y = AvgGdpPercap)) +
geom_line(color = "#f96000", size = 2) +
geom_point(color = "#f96000", size = 5) +
geom_label(
aes(label = AvgGdpPercap),
nudge_x = 0.25,
nudge_y = 0.25
) +
labs(title = paste("Average GDP per capita in", input$inContinent)) +
theme_classic() +
chart_theme
})
}
<br>shinyApp(ui = ui, server = server)</code></pre>
Here's what you'll see once you run the app:
<img class="size-full wp-image-21607" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae26c0dc47f4ac1d8de9_f8d1eacc_4.webp" alt="Image 4 - Simple R Shiny application" width="2009" height="1487" /> Image 4 - Simple R Shiny application
As mentioned earlier, the chart values are recalculated as soon as you change the value of a continent dropdown menu:
<img class="size-full wp-image-21609" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae2743e636d0b1fff794_3598bde2_5.webp" alt="Image 5 - Simple R Shiny application (2)" width="2009" height="1487" /> Image 5 - Simple R Shiny application (2)
Up next, let's explore the containerization process with Docker.
<h3>Containerizing R Shiny App with Docker</h3>
Containerization of Shiny apps with Docker boils down to selecting an existing image, installing your application's specific dependencies, exposing the application port, and running the R file.
As for the image, <code>rocker/shiny</code> ships with R and Shiny installed. Then, you can install all R packages that are required to run the application, create a directory for the app, copy the <code>app.R</code> file, expose the port, and use <code>Rscript</code> to run <code>app.R</code> by specifying the full path.
All of the mentioned logic has to be written in a <code>Dockerfile</code>, which is a file used by Docker to build new images:
<pre><code class="language-dockerfile"># Base R Shiny image
FROM rocker/shiny
<br># Make a directory in the container
RUN mkdir /home/shiny-app
<br># Install R dependencies
RUN R -e "install.packages(c('dplyr', 'ggplot2', 'gapminder'))"
<br># Copy the Shiny app code
COPY app.R /home/shiny-app/app.R
<br># Expose the application port
EXPOSE 8180
<br># Run the R Shiny app
CMD Rscript /home/shiny-app/app.R</code></pre>
To actually build the image, open a new Terminal window and navigate to where both <code>app.R</code> and <code>Dockerfile</code> are stored. Then, run the following:
<pre><code class="language-bash">docker build -t shiny-app .</code></pre>
As a side note, you'll want to specify the <code>platform</code> parameter if you're building this image on Apple Silicon devices:
<pre><code class="language-bash">docker build --platform linux/x86_64 -t shiny-app .</code></pre>
This will build an image with a tag of <code>shiny-app</code>:
<img class="size-full wp-image-21611" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae2874fe971f0384d44c_1a43e508_6.webp" alt="Image 6 - Building a Docker image" width="1336" height="787" /> Image 6 - Building a Docker image
And now to run it in a Docker container, run the following:
<pre><code class="language-bash">docker run -p 8180:8180 shiny-app</code></pre>
This will bind the container's port 8180 to the local 8180 port:
<img class="size-full wp-image-21613" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae2983dae004bbf79091_b12507e5_7.webp" alt="Image 7 - Running a Docker container" width="1336" height="787" /> Image 7 - Running a Docker container
Which in turn means you can access the app on <code>http://0.0.0.0:8180</code>:
<img class="size-full wp-image-21615" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae2a43e636d0b1fff96c_ef329609_8.webp" alt="Image 8 - R Shiny application containerized with Docker" width="1526" height="1184" /> Image 8 - R Shiny application containerized with Docker
Next, let's do the same with Podman.
<h3>Containerizing R Shiny App with Podman</h3>
If you already have experience using Docker, you'll be pleased to know Podman <strong>can work based on the identical <code>Dockerfile</code>s</strong>. So if you haven't already, create one right where your <code>app.R</code> is and paste the following:
<pre><code class="language-dockerfile"># Base R Shiny image
FROM rocker/shiny
<br># Make a directory in the container
RUN mkdir /home/shiny-app
<br># Install R dependencies
RUN R -e "install.packages(c('dplyr', 'ggplot2', 'gapminder'))"
<br># Copy the Shiny app code
COPY app.R /home/shiny-app/app.R
<br># Expose the application port
EXPOSE 8180
<br># Run the R Shiny app
CMD Rscript /home/shiny-app/app.R</code></pre>
We've explained what these instructions do, but here's a recap: You're pulling a base image that has R and Shiny installed, then installing more dependencies that are needed to run your Shiny app, then copying the application file, exposing the port, and running the app with <code>Rscript</code>.
To build the image, run the following command:
<pre><code class="language-bash">podman build -t shiny-app .</code></pre>
Similarly to Docker, you'll want to specify the <code>platform</code> parameter if you're building this image on Apple Silicon devices:
<pre><code class="language-bash">podman build --platform linux/x86_64 -t shiny-app .</code></pre>
This is the output you will see:
<img class="size-full wp-image-21617" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae2be97e1ea9c975dfb9_247b2bab_9.webp" alt="Image 9 - Building a Podman image" width="1336" height="783" /> Image 9 - Building a Podman image
Now to run the Shiny app, execute the following:
<pre><code class="language-bash">podman run -p 8180:8180 shiny-app</code></pre>
This will run the app locally on port 8180:
<img class="size-full wp-image-21619" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae2c43e636d0b1fffa99_07b0a09a_10.webp" alt="Image 10 - Running the Shiny app with Podman" width="1336" height="783" /> Image 10 - Running the Shiny app with Podman
Let's verify everything works by opening the app:
<img class="size-full wp-image-21621" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae2d26580881d679983b_70cd9c80_11.webp" alt="Image 11 - R Shiny application containerized with Podman" width="1551" height="1226" /> Image 11 - R Shiny application containerized with Podman
Finally, let's see how containerization works with Singularity.
<h3>Containerizing R Shiny App with Singularity</h3>
Singularity natively runs on Linux, but there are ways to <a href="https://docs.sylabs.io/guides/3.5/admin-guide/installation.html#installation-on-windows-or-mac" target="_blank" rel="noopener noreferrer">install it on Windows and Mac</a>. Currently, there's no viable way (except UTM) to get it up and running on Apple Silicon Macs, so that might be a deal-breaker for some. Still, Singularity is designed with high-performance computing in mind, and we doubt many such environments will be running something else than Linux.
It's also worth noting that <strong>Singularity can't use <code>Dockerfile</code>s directly to build images, so you'll have to translate it into a Definition file first.</strong> Here are the contents of the <code>Dockerfile</code>:
<pre><code class="language-dockerfile"># Base R Shiny image
FROM rocker/shiny
<br># Make a directory in the container
RUN mkdir /home/shiny-app
<br># Install R dependencies
RUN R -e "install.packages(c('dplyr', 'ggplot2', 'gapminder'))"
<br># Copy the Shiny app code
COPY app.R /home/shiny-app/app.R
<br># Expose the application port
EXPOSE 8180
<br># Run the R Shiny app
CMD Rscript /home/shiny-app/app.R</code></pre>
You now have to build a Docker image first and be a bit more specific with the tagging:
<pre><code class="language-bash">sudo docker build -t local/shiny-app:latest .</code></pre>
<img class="size-full wp-image-21623" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae2de3a0d26c065f400a_dc7c4f25_12.webp" alt="Image 12 - Building a Docker image" width="2708" height="1578" /> Image 12 - Building a Docker image
The next step is to create a Singularity container from a locally built Docker image. Run the following command:
<pre><code class="language-bash">sudo singularity build shiny-app.sif docker-daemon://local/shiny-app:latest</code></pre>
Here's what you'll see in your Terminal window:
<img class="size-full wp-image-21625" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae2e26580881d67998e3_6e8fa7b8_13.webp" alt="Image 13 - Building a Singularity container" width="2708" height="1578" /> Image 13 - Building a Singularity container
This will create a Singularity Image File (SIF) called <code>shiny-app.sif</code>.
To run the container, execute the following:
<pre><code class="language-bash">singularity run shiny-app.sif</code></pre>
This will run the app on port 8180:
<img class="size-full wp-image-21627" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae2f453845f0c8da04bf_36dd54f5_14.webp" alt="Image 14 - Running the Shiny app with Singularity" width="2708" height="1578" /> Image 14 - Running the Shiny app with Singularity
As before, you can open the app via your web browser to verify everything works:
<img class="size-full wp-image-21629" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b7ae3081d951505be703f8_d86d972b_15.webp" alt="Image 15 - R Shiny application containerized with Singularity" width="1729" height="1274" /> Image 15 - R Shiny application containerized with Singularity
And that's how you can containerize an R Shiny application with Singularity. It's a bit more work, but nothing you can't manage.
<b>But which platform is the best?</b> The answer is highly subjective, but let's try to address it next.
<hr />
<h2 id="summary">Summing up Docker vs. Podman vs. Singularity - Which Platform is Best for You?</h2>
When it comes to simplicity, developer experience, and company adoption, Docker is hard to beat. It's by far the most popular containerization platform currently available and is often used as a synonym for containerization.
That being said, Docker isn't free to use for large organizations. There are also some monthly subscription plans for individuals, but we haven't touched on these yet. Podman might be a better option for you if you want an open-source and free containerization platform.
On the other hand, if you are working in an environment that requires high-performance computing, look no further than Singularity.
In short, pick:
<ul><li><b>Docker</b> if you want the best support, a huge amount of documentation and examples, and don't mind the cost (only applies to companies with more than 250 employees or more than $10M in revenue),</li><li><b>Podman</b> if you want a free open-source product that looks and feels a lot like Docker, so the learning curve is somewhat minimal,</li><li><b>Singularity</b> if you have containerization needs in a high-performance computing environment.</li></ul>
From a life science perspective, pick:
<ul><li><strong>Docker:</strong> If you are seeking a comprehensive ecosystem with abundant resources and tools. Docker’s corporate backing ensures continuous development and support, but the absence of rootless containers might be a concern for sensitive data handling.</li><li><strong>Podman:</strong> If you value open-source and heightened security. Podman’s modular design, while offering more control, might require additional setup and management.</li><li><strong>Singularity:</strong> If you are working on applications involving HPC, such as genomic sequencing or protein structure simulations. Its security features and design are tailored for scientific computing, making it a strong contender for research-intensive life science projects.</li></ul>
<blockquote>Found this helpful? Get the latest insights on deploying R Shiny apps delivered to your inbox weekly with <a href="https://appsilon.us16.list-manage.com/subscribe?u=c042d7c0dbf57c5c6f8b54598&id=870d5bfc05" target="_blank" rel="noopener">Shiny Weekly</a>.</blockquote>