7  Docker Image Build Guide for Shiny Apps

7.1 Docker Build Guide for Shiny Apps

This guide explains how to containerize your Shiny app using Docker. The process varies slightly depending on whether your app is built with {golem}, {rhino}, or as a bare Shiny app script.

7.1.1 How to Build Docker Image of Your App for Further Deployment

7.1.1.1 Prerequisites

Before you begin, make sure:

  • ✅ You have an up-to-date renv.lock file in your project directory.
    This is very important!
  • ✅ Docker Engine is installed and running on your machine.

7.1.1.1.1 🧱 Example 1: If You Are Using {golem}
# Default base image with Shiny Server
FROM rocker/shiny:4.4.1

# Note:
# If you encounter issues with rocker/shiny, you can try:
# FROM rocker/r-ver:4.4.1

# Install system dependencies
RUN apt-get update && apt-get install -y \
  dotnet-runtime-8.0 \
  libcurl4-openssl-dev \
  libssl-dev \
  libxml2-dev \
  libfontconfig1-dev \
  libfreetype6-dev \
  libpng-dev \
  libtiff5-dev \
  libjpeg-dev \
  git
RUN ln -s /usr/lib/x86_64-linux-gnu/libdl.so.2 /usr/lib/x86_64-linux-gnu/libdl.so

# Copy the app package
COPY . /app
WORKDIR /app

# Install renv and restore environment
RUN R -e "install.packages('renv'); \
          options(renv.config.repos.override = 'https://packagemanager.posit.co/cran/latest'); \
          renv::restore();"

# Install your golem package
RUN R -e "renv::install('.')"

# Expose Shiny port
EXPOSE 80

# Start {golem} app (replace <your_package_name> with actual name)
CMD ["R", "-e", "shiny::runApp(<your_package_name>::run_app(), host='0.0.0.0', port=80)"]

7.1.1.1.2 🦏 Example 2: If You Are Using {rhino}
FROM rocker/shiny:4.4.1

# Install system dependencies
RUN apt-get update && apt-get install -y \
  dotnet-runtime-8.0 \
  libcurl4-openssl-dev \
  libssl-dev \
  libxml2-dev \
  libfontconfig1-dev \
  libfreetype6-dev \
  libpng-dev \
  libtiff5-dev \
  libjpeg-dev \
  git

RUN ln -s /usr/lib/x86_64-linux-gnu/libdl.so.2 /usr/lib/x86_64-linux-gnu/libdl.so

COPY . /app
WORKDIR /app

RUN R -e "install.packages('renv'); \
          options(renv.config.repos.override = 'https://packagemanager.posit.co/cran/latest'); \
          renv::restore();"

EXPOSE 80

# Start {rhino} app
CMD ["R","-e", "shiny::runApp('./app.R', host='0.0.0.0', port = 80)"]

7.1.1.1.3 🧩 Example 3: If You Are Using a Bare Shiny App (not a package)
FROM rocker/shiny:4.4.1

RUN apt-get update && apt-get install -y \
  dotnet-runtime-8.0 \
  libcurl4-openssl-dev \
  libssl-dev \
  libxml2-dev \
  libfontconfig1-dev \
  libfreetype6-dev \
  libpng-dev \
  libtiff5-dev \
  libjpeg-dev \
  git
RUN ln -s /usr/lib/x86_64-linux-gnu/libdl.so.2 /usr/lib/x86_64-linux-gnu/libdl.so

COPY . /app
WORKDIR /app

RUN R -e "install.packages('renv'); \
          options(renv.config.repos.override = 'https://packagemanager.posit.co/cran/latest'); \
          renv::restore();"

EXPOSE 80

# Start bare Shiny app
CMD ["R", "-e", "shiny::runApp('./app.R', host='0.0.0.0', port=80)"]

7.1.1.1.4 🔐 Optional: Enable Access to Private GitHub Repositories

If your Shiny app depends on private R packages hosted on GitHub, you’ll need to pass a GitHub token to your Dockerfile.

Dockerfile Instructions

Add the following lines to your Dockerfile after the WORKDIR /app line:

# Pass GitHub token for private repos
ARG GITHUB_PAT
ENV GITHUB_PAT=${GITHUB_PAT}

This allows renv::restore() or other commands like remotes::install_github() to authenticate and install private packages during the Docker build process.

🚩 Important: Only include this section if your project uses private GitHub dependencies. Otherwise, it’s unnecessary and can be safely omitted.


📝 Note about base image and R version:
The image tag in the FROM line (e.g., rocker/shiny:4.4.1 or rocker/r-ver:4.4.1) must match the R version in your renv.lock file.
If you’re not sure, open renv.lock and check:

{
  "R": {
    "Version": "4.4.1"
  }
}

If the tag and version don’t match, your build may fail or produce unexpected results. example:

FROM rocker/shiny:<R version from `renv.lock` file>

P.S. If you encounter issues with rocker/shiny base image, you can try:

FROM rocker/r-ver:<R version from `renv.lock` file>

7.1.1.2 🛠 Build and Run Instructions

To build the Docker image:

docker build -t your-app-name .

ℹ️ your-app-name is a tag you choose. It can be any name.

To run the image locally:

docker run --rm -p 3838:80 your-app-name

🔁 You can replace 3838 with any local port,
but must keep 80 inside the container.

Once running, open your browser and go to:

http://localhost:3838

If your app loads — 🎉 you’re ready for further deployment (e.g., to Azure App Service)!

7.2 How to Deploy a Docker Image of Your App to Azure

  1. Sign in to the Azure portal with your Microsoft Account and 2FA code.
  2. Search “App Services”.
  3. Select “Create”.
  4. Select “Web App”.
  5. Follow the configuration procedure:
    • Select the Resource Group Shiny.
    • Type the Web App Name.
    • Select Publish in Container.
    • Select Operating System Linux.
    • Select Region Germany West Central.
    • Click on Next: Database.
    • Click on Next: Container.
    • Select the correct Image Source (Azure Registry or Docker Registry).
    • Enter the URL of the registry, Image name and Tags, and Optional Startup command if needed.
    • Click on Next: Networking.
    • Select Enable Public Access: On.
    • Go to the tab `Review and Create’.
    • Deploy your Web App.

7.2.1 Additional Azure Configration

If you need to add a Custom Domain URL follow steps below: 1. Login at IONOS.de with admin account. 2. Create a dedicated CNAME and TXT record (Values from the Azure Portal: Custom Domain). 3. Validate your records in the Azure Portal by clicking on Validate Custom Domain. 4. Utilize default Azure SSL Certificate and SNI Binding. 5. After a couple of minutes you can browse your Web App in your custom domain.

7.3 Automate Docker Build & Push to Azure with GitHub Actions

You can automate the Docker image build and push process to Azure Container Registry (ACR) using GitHub Actions. This is especially useful for continuous deployment pipelines when pushing to the main branch.

7.3.1 Prerequisites

Before you use the workflow, make sure:

  • Azure Container Registry (ACR) is set up and accessible.
  • You have access/created the following secrets in your GitHub repository or organization:
    • ACR_ENDPOINT: e.g. myregistry.azurecr.io
    • ACR_USERNAME: the username for your ACR
    • ACR_PASSWORD: the password for your ACR
    • ACTIONS_PAT (optional): only needed if your project depends on private GitHub repositories (e.g., private R packages).

7.3.2 GitHub Actions Workflow Example

Save this in .github/workflows/docker-image.yml:

name: Build and Push Docker Image to Azure

on:
  push:
    branches: [ main ]

env:
  IMAGE_NAME: your-image-name  # 🔧 You can change this to any name you like

jobs:
  build-and-push:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Azure Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ secrets.ACR_ENDPOINT }}
          username: ${{ secrets.ACR_USERNAME }}
          password: ${{ secrets.ACR_PASSWORD }}

      - name: Build and Push Docker Image
        uses: docker/build-push-action@v5
        with:
          context: .
          file: Dockerfile
          push: true
          tags: ${{ secrets.ACR_ENDPOINT }}/${{ env.IMAGE_NAME }}:latest
          # 🔒 Optional: only include build-args if you use private GitHub dependencies
          build-args: |
            GITHUB_PAT=${{ secrets.ACTIONS_PAT }}

🛠️ The IMAGE NAME is customizable — name your image however you like.

🔐 The build-args section is optional and should only be included if your build process accesses private GitHub repositories (e.g., installing a private R package via renv::restore()).

📦 This workflow builds and pushes the Docker image on every push to the main branch. Your Azure App Service can then pull this image from ACR for deployment.