
Generate a reproducible Dockerfile for an R project
Source:R/generate-dockerfile.R
generate_dockerfile.Rdgenerate_dockerfile() inspects an R project's dependencies via an renv
lockfile and writes a ready-to-use Dockerfile to the specified output
directory. It supports multiple Rocker base images, automatic system
library detection, Quarto installation, file copying, user creation, and
inline documentation comments.
Usage
generate_dockerfile(
r_version = "current",
r_mode = "base",
auto_syslibs = TRUE,
install_syslibs = NULL,
output = ".",
data_file = NULL,
code_file = NULL,
misc_file = NULL,
add_user = NULL,
home_dir = "/home",
expose_port = "8787",
install_quarto = FALSE,
quarto_version = "latest",
os_version = NULL,
comments = FALSE,
verbose = FALSE,
config = NULL
)Arguments
- r_version
A character string specifying the R version to use, e.g.
"4.3.0". Defaults to"current", which resolves to the version of R running in the current session.- r_mode
A character string selecting the Rocker base image. Inspired by the Rocker Project. One of
"base"for plain R,"tidyverse"for R with the tidyverse,"rstudio"for RStudio Server,"verse"for tidyverse plus TeX Live and publishing-related packages,"shiny_server"for serving Shiny apps, or"rstudio_shiny"for RStudio Server with Shiny Server layered on top. Defaults to"base".- auto_syslibs
Logical. If
TRUE(the default), readsrenv.lockfrom the current working directory, queries the Posit Package Manager sysreqs database viaremotes::system_requirements(), and automatically includes the system libraries required by all packages in the lock file. Warns and continues without auto-detection if the lookup fails. Set toFALSEto skip auto-detection entirely.- install_syslibs
A character vector or
NULL. Additional system libraries to install beyond those auto-detected fromrenv.lock. Each element should be a validaptpackage name, e.g.c("libuv1-dev", "libwebp-dev"). Defaults toNULL.- output
A character string. Directory path where the
Dockerfilewill be written. Defaults to".", the current working directory – the same directorybuild_image()treats as the build context by default, so the two functions' defaults compose without either argument having to be supplied. ADockerfilewritten somewhere else (tempdir(), say) would have to be moved into the build context beforebuild_image()could find it, since the build context is alwaysgetwd().outputis created automatically, along with any missing parent directories, if it does not already exist.- data_file
A character vector or
NULL. Path(s) to data file(s) and/or directories to copy into the container – a single path, a vector of paths, or a directory (copied whole, with its contents) may all be mixed freely in the same vector. The local directory structure is preserved underhome_dirfor"base","tidyverse","rstudio", and"verse"(e.g. with the defaulthome_dir = "/home","data-raw/sample.csv"becomes/home/data-raw/sample.csv, and a directory"data-raw/"is copied to/home/data-raw/in full), or under/srv/shiny-server/for"shiny_server"and"rstudio_shiny", matching Shiny Server's own default app directory regardless ofhome_dir. Every path must be inside the current working directory (the build context). Defaults toNULL.- code_file
A character vector or
NULL. Path(s) to script file(s) (e.g..R,.qmd,.rmd) and/or directories to copy into the container – seedata_filefor vector and directory behavior. The local directory structure is preserved under the mode's copy root (home_dirfor four of the six modes) – seedata_file. Every path must be inside the current working directory. Defaults toNULL. Leave it out for an image that will run cluster jobs: in that workflow the analysis script travels with each job (submitruploads it alongside the data subset), so the image holds the environment and the data, and editing the script never means rebuilding the image. Passcode_filewhen the image itself has to run the code – a Shiny app, or a self-contained image handed to a collaborator.confignever fills it in; seeconfig.- misc_file
A character vector or
NULL. Path(s) to miscellaneous file(s) (e.g. images, shell scripts, or branding assets) and/or directories to copy into the container – seedata_filefor vector and directory behavior. The local directory structure is preserved under the mode's copy root (home_dirfor four of the six modes) – seedata_file. Every path must be inside the current working directory. Defaults toNULL. If the project was scaffolded withtoolero::init_project()usingbranding = TRUEorbranding = "uw-madison", the generated.qmdwill referenceassets/styles.css,assets/header.html, andassets/footer.htmlat render time. Those files must be present inside the container or Quarto will error on render. Passmisc_file = "assets/"to copy the entire branding folder in one step. Additional files and directories can be combined freely in the same vector, e.g.misc_file = c("assets/", "extra-script.sh").- add_user
A character string. Name of a Linux user to create inside the container with sudo access. Defaults to
NULL.- home_dir
A character string. The working directory set inside the container via
WORKDIR. For"base","tidyverse","rstudio", and"verse", this is also wheredata_file,code_file, andmisc_fileare copied (C24) – there is nowhere else a script running fromWORKDIRcould resolve its relative paths against."shiny_server"and"rstudio_shiny"are the exception: their copy destination is fixed at/srv/shiny-serverregardless ofhome_dir– seedata_file. Defaults to"/home".- expose_port
A character string. Overrides the port exposed when
r_modeis"rstudio". Defaults to"8787". Ignored for every otherr_mode–"shiny_server"and"rstudio_shiny"expose their own fixed port(s) ("3838", and"8787"/"3838"respectively), since a single override value can't address more than one port.- install_quarto
Logical. If
TRUE, downloads and installs the Quarto CLI inside the container. Defaults toFALSE. Seequarto_versionto pin a specific release rather than always installing whatever is currently latest.- quarto_version
Character string. Either
"latest"(the default) or an explicit Quarto version, e.g."1.5.57". Ignored unlessinstall_quarto = TRUE. When"latest", the actual version is resolved at generation time via the Quarto releases API and recorded in the generatedDockerfileasENV QUARTO_VERSION=..., so a later rebuild from the sameDockerfilereproduces the same Quarto version rather than whatever happens to be current at build time – consistent with howr_versionandrenv.lockare pinned elsewhere in the image. An explicit version is validated against the Quarto releases API and errors if no matching release exists.- os_version
A character string or
NULL. The Ubuntu version to query against when looking up system requirements forauto_syslibs(C14). WhenNULL(the default), it is derived from the resolvedr_versionvia the Rocker Project's own R-version-to-Ubuntu-release mapping – see.resolve_os_version()– rather than left at a single hardcoded value that only matched the Ubuntu release actually backing some R versions and not others. Supplying a value overrides the derivation entirely, for a project that needs to query against a different Ubuntu release than the one itsr_versionwould normally resolve to. Ignored whenauto_syslibs = FALSE, since no sysreqs lookup happens in that case.- comments
Logical. If
TRUE, annotates each Dockerfile instruction with an explanatory comment, written on the line above the instruction it describes. Useful for learning or sharing. Defaults toFALSE. Note (C15): this is the onecommentsargument in the family that writes into a generated file rather than printing to the console –build_image()andpush_image()in this same package, and everycommentsargument insubmitr, use it to print explanatory guidance to the console instead. Keep that distinction in mind when moving between these functions. The comment-then-instruction ordering matchessubmitr's own generated.shand.subfiles (S09), so a reader moving between a generated Dockerfile and a submitr-generated script reads both the same way round: explanation first, instruction second.- verbose
Logical. If
TRUE, prints progress messages as each section of the Dockerfile is written. Defaults toFALSE.- config
A character string. Path to a
_toolero.ymlproject config, such as the onetoolero::init_project()writes. When supplied, fills indata_fileandmisc_filefrom the config's declaredfolders:– but only an argument left at its ownNULLdefault. An argument you do supply always wins;confignever overrides an explicit call.data_fileis derived from"data-raw"when present, the folder this family's own documentation uses as the canonical home for input data – there is no dedicated config key naming it, so this one iscontainr's own convention rather than something the file states explicitly;misc_fileis derived from"assets"when present, the branding folderinit_project(branding = ...)creates.code_fileis deliberately never derived, and the config'sscript_dirconvention is not consulted: the analysis script travels with each cluster job rather than living in the image (seecode_file), so a script copied in because the project happens to have anR/folder would be a second, stale copy that nothing runs. Passcode_fileyourself when the image does need it. Reading_toolero.ymlis not a dependency ontoolero: the schema is the contract, and a config written by hand is as valid an input as oneinit_project()created. Inverbosemode, reports which ofdata_fileandmisc_filecame fromconfigrather than from the call, and notes thatcode_filedid not, since a generatedDockerfilewhoseCOPYlines came from somewhere invisible to the caller undermines the reproducibility this package exists to support. Aschema_versionthe file does not declare is treated as schema1; any other declared value produces a warning, not an abort, and the file is still read on a best-effort basis either way. When supplied,configalso adds aRUN mkdir -pinstruction, right afterWORKDIR, creating every folder the config'sfolders:declares underhome_dir(C07) – so a script's first write into one of them (viatoolero::save_output(), or a bareggsave()/write.csv()call) does not fail the way it would on a fresh checkout with noconfigsupplied. This is unconditional on which folders those are; a folder this version ofcontainrhas no other special meaning for is still created. Defaults toNULL, so nothing about this argument changes the behavior of a call that does not use it.
Prerequisites
generate_dockerfile() requires an renv.lock file in the current working
directory. Create one with renv::snapshot() before calling this function.
If the lock file is out of sync with your project library, a warning is
issued – run renv::snapshot() to update it before building the image.
If the project uses Quarto with branding assets (i.e. toolero::create_qmd()
was called with use_style = TRUE), the assets/ folder must be copied
into the container alongside the .qmd file or Quarto will be unable to
resolve the CSS and HTML includes at render time. The simplest way to
ensure this is to pass misc_file = "assets/" – or
misc_file = c("assets/", other_files) if additional files are needed –
when calling generate_dockerfile(). No code change is required;
misc_file already accepts directories and copies them whole.
Examples
if (FALSE) { # \dontrun{
# Requires renv.lock in the current working directory.
# Run renv::snapshot() first if you don't have one.
# Generate a minimal Dockerfile using a pinned R version. output defaults
# to ".", so this writes to the current working directory -- the same
# place build_image() looks by default.
generate_dockerfile(r_version = "4.4.0")
# Pin a specific R version with the tidyverse image
generate_dockerfile(r_version = "4.3.0", r_mode = "tidyverse")
# Add extra system libraries on top of auto-detected ones
generate_dockerfile(
r_version = "4.4.0",
install_syslibs = c("libuv1-dev", "libwebp-dev"),
output = "."
)
# Include a data file -- directory structure is preserved in the container.
# No code_file: for cluster jobs the script travels with each job.
generate_dockerfile(
r_version = "4.3.0",
data_file = "data-raw/penguins.csv",
comments = TRUE,
output = "."
)
# Several data files and a whole assets folder -- pass assets/ via
# misc_file so that branding files (styles.css, header.html, footer.html)
# are present inside the container when Quarto renders the .qmd
generate_dockerfile(
r_version = "4.3.0",
data_file = c("data-raw/penguins.csv", "data-raw/islands.csv"),
misc_file = "assets/",
output = "."
)
# Serve a Shiny app -- the image runs the app itself, so its code goes in
# via code_file. Files land under /srv/shiny-server/ automatically
generate_dockerfile(
r_version = "4.3.0",
r_mode = "shiny_server",
code_file = "app.R",
output = "."
)
# RStudio Server plus Shiny Server in the same image
generate_dockerfile(
r_version = "4.3.0",
r_mode = "rstudio_shiny",
code_file = "app.R",
output = "."
)
# Install Quarto, pinned to a specific release rather than whatever is
# currently latest -- the resolved version is recorded as
# ENV QUARTO_VERSION in the generated Dockerfile either way
generate_dockerfile(
r_version = "4.3.0",
install_quarto = TRUE,
quarto_version = "1.5.57",
output = "."
)
# Fill in data_file and misc_file from a toolero project config instead
# of retyping paths the project already declares (code_file is never
# filled in from config)
generate_dockerfile(
r_version = "4.3.0",
config = "_toolero.yml",
output = "."
)
} # }