Skip to content

faucet in Containers (Docker)

Probably the easiest and most versatile way to deploy faucet is to build a Linux container image and run it in a container. This will allow you to run faucet on any Linux host that supports containers, like a laptop, a VM, or a server.

Build a Docker Image for faucet

In this section, you will be using the available faucet + R image from the Docker Hub. You can however, build your own image if you want to. You should use the available installation instructions for your OS to install Docker.

In order to properly build the faucet image, you will need to take the following steps into account:

  1. Install Docker on your host machine. You can find instructions on how to do this for your specific OS in the Docker Installation Guide.
  2. Take your R application dependencies into account. If you are using R packages that require system dependencies, you will need to install them in the Docker image. Also, take the R and library versions into account, we highly recommend you use renv. For this tutorial we will assume you are using renv already.
  3. Ignore sensitive or unnecessary files. You can use a .dockerignore file to ignore files that are not needed in the Docker image or you can manually specify the files you want to include in the image. In this case, we will use a .dockerignore file to ignore said files.

A basic Shiny app or Plumber API

In this section, you will bootstrap a basic Shiny app or Plumber API to use as an example. You can use your own app or API, but make sure you have renv initialized.

Shiny app

# app.R
library(shiny)

ui <- fluidPage(
  titlePanel("Hello Shiny!"),
  sidebarLayout(
    sidebarPanel(
      sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100)
    ),
    mainPanel(
      plotOutput("distPlot")
    )
  )
)

server <- function(input, output) {
  output$distPlot <- renderPlot({
    hist(rnorm(input$obs))
  })
}

shinyApp(ui = ui, server = server)

After saving the app, you can run it locally with:

shiny::runApp()

To make absolutely sure that renv detects all the packages used in the app, you should create a dependencies.R file with the following contents:

# dependencies.R
library(shiny)

Now, you can initialize renv and install the packages:

renv::init()
renv::activate()

Plumber API

# plumber.R
#* @get /echo
function(){
  list(msg = "Hello World!")
}

After saving the API, you can run it locally with:

library(plumber)
# 'plumber.R' is the location of the file shown above
pr("plumber.R") %>%
  pr_run()

To make absolutely sure that renv detects all the packages used in the API, you should create a dependencies.R file with the following contents:

# dependencies.R
library(plumber)

Now, you can initialize renv and install the packages:

renv::init()
renv::activate()

Dockerfile

Dockerignore

The first step to building our Docker image is to create a .dockerignore file in the root of our project. This file will contain the files that you want to ignore when building the Docker image. In this case, we will ignore the following renv files:

renv/library/
renv/local/
renv/cellar/
renv/lock/
renv/python/
renv/sandbox/
renv/staging/

If this were a real project, you would probably also ignore files like .git, .Rproj.user, .DS_Store, and sensitive files like .env, .htpasswd, etc.

Writing the Dockerfile

The first step to building our Docker image is to create a Dockerfile in the root of our project. This file will contain the instructions to build our Docker image. In this case, you will use the ixpantia/faucet image as our base image. This image is based on the rocker/r-ver image, which is a minimal R image based on Debian Linux.

FROM ixpantia/faucet:r4.3

# Some environment variables to tell `renv`
# to install packages in the correct location
# and without unnecessary symlinks
ENV RENV_CONFIG_CACHE_SYMLINKS FALSE
ENV RENV_PATHS_LIBRARY /srv/faucet/renv/library

# You copy the necessary files to bootstrap `renv`
COPY ./renv.lock .
COPY ./renv ./renv
COPY ./.Rprofile .

# You install the packages
RUN Rscript -e "renv::restore()"

# Copy the app/API files in this case replace
# `app.R` with `plumber.R` if you are using a
# Plumber API
COPY ./app.R .

# You can run the container as a non-root user
# for security reasons if we want to though
# this is not necessary. You could ignore this
RUN chown -R faucet:faucet /srv/faucet/
USER faucet

Building the Docker image

Now that you have a Dockerfile and a .dockerignore file, you can build the Docker image with the following command:

docker build -t my_faucet_app .

Running the Docker image

Once the image is built, you can run it with the following command:

docker run --rm -p 3838:3838 my_faucet_app

You can now access your app/API at http://localhost:3838.

Controlling the faucet instance

You can control every aspect of the faucet instance by setting environment variables in the Docker container. For example, if you want to change the number of workers, you can do so by setting the FAUCET_WORKERS environment variable:

docker run --rm -p 3838:3838 -e FAUCET_WORKERS=4 my_faucet_app

If you are running the app/API behind a proxy like Nginx, you can set the FAUCET_IP_FROM environment variable to x-real-ip or x-forwarded-for to make sure faucet gets the correct IP address of the client.

docker run --rm -p 3838:3838 -e FAUCET_IP_FROM=x-real-ip my_faucet_app