{"id":13857591,"url":"https://github.com/robertdj/renv-docker","last_synced_at":"2025-04-12T02:35:53.443Z","repository":{"id":85449188,"uuid":"236571395","full_name":"robertdj/renv-docker","owner":"robertdj","description":"A guide to getting {renv} projects into Docker images","archived":false,"fork":false,"pushed_at":"2022-05-29T14:14:45.000Z","size":91,"stargazers_count":54,"open_issues_count":0,"forks_count":6,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-12T02:35:48.775Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/robertdj.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-01-27T19:12:19.000Z","updated_at":"2025-01-14T06:28:44.000Z","dependencies_parsed_at":null,"dependency_job_id":"d0d92083-abd2-434a-bac9-8abe2753b44f","html_url":"https://github.com/robertdj/renv-docker","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertdj%2Frenv-docker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertdj%2Frenv-docker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertdj%2Frenv-docker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/robertdj%2Frenv-docker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/robertdj","download_url":"https://codeload.github.com/robertdj/renv-docker/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248506966,"owners_count":21115515,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-05T03:01:41.383Z","updated_at":"2025-04-12T02:35:53.379Z","avatar_url":"https://github.com/robertdj.png","language":"R","readme":"renv-docker\n===========\n\nThe [{renv} package](https://rstudio.github.io/renv) provides isolated projects by having a package library for each project.\nSo even after updating packages in your main R library, packages in an renv'ed project are not affected.\n\nThis repository show how to import renv'ed projects into *self-contained* Docker images -- building on ideas from the article [\"Using renv with Docker\" at {renv}'s website](https://rstudio.github.io/renv/articles/docker.html).\n\n**Please note**: The `Dockerfile`s will most likely not run as is because the path to {renv}'s cache on the host is a path on my computer (`/home/robert/code/R/renv-cache`).\nBut change the path in the `renv_install.sh` scripts and it should work.\n\nTo see the path to {renv}'s cache run `renv::paths$cache()`.\nThe path can be changed by setting the environment variable `RENV_PATHS_CACHE` like in the `renv_install.sh` scripts.\n\n\n# What problem am I trying to solve?\n\nThere is no technical problem in copying an renv'ed project into a Docker image and restoring it.\nHowever, installing all of a project's dependencies can be time consuming on Linux (which is what we use in Docker) since all packages have to be compiled from scratch.\nAs an example, a full tidyverse can easily take 15 minutes to install.\n\nThe {renv} package circumvents this by having its own cache with packages.\nPackages used in renv'ed projects are installed in the cache and a symbolic link/shortcut is made from the renv'ed projects to the cache.\n\nWhen making Docker images we want them to be *self-contained*, so that they can run on any host.\nSo my problem is how to build self-contained Docker images *fast*, that is, using a cache.\n\n\nA nice side-effect of installing packages in the manner described here is that it is easy to include packages from private CRANs requiring authentication.\nMore details are provided later.\n\n\n# A solution\n\nThe aforementioned article from {renv}'s website suggests not installing packages in the image, but on the host and then allow a container created from the image to mount {renv}'s cache on the host when it runs.\nHowever, such an image is not self-contained.\n\nMy solution in this repository is to create two Docker images: \n\n- The \"install image\": The first image consists only of the prerequisites for the projects. When running a container from this image it can install R packages in the format it needs *inside the container* and save them to {renv}'s cache on the host through a mount.\n- The \"final image\": The second image copies the project along with dependencies from the host into the image.\n\nNote that when {renv}'s cache on the host is filled in this manner it contains Linux versions of the packages, even if the host operating system is not Linux.\n\n\n# Demo projects\n\nThere are three demo projects, each in its own folder with an associated RStudio project and {renv} setup.\n\nThe examples with Shiny server use a simple configuration file with an elaborate URL for the demo app.\nCheck out [Shiny server's docs](https://docs.rstudio.com/shiny-server) to learn more about its configuration.\n\n\n## Here\n\nThis project is very simple.\nIt contains a single R script loading the [{here} package](https://cran.r-project.org/package=here).\nDue to the minimal requirements of the {here} package, the \"install image\" just sets the working directory.\n\nThe path to reconstruction is:\n\n1. Navigate to the `here` folder.\n2. Build the \"install image\":\n\n```\ndocker build --build-arg R_VERSION=4.1.1 --tag renv-test:latest -f Dockerfile_install .\n```\n\n3. Restore the project inside the container by running the `renv_install.sh` script.\n4. Build the final image:\n\n```\ndocker build --build-arg R_VERSION=4.1.1 --tag renv-test:latest .\n```\n\nCheck out a running container with this command:\n\n```\ndocker run --rm -it renv-test:latest\n```\n\nYou should see {renv} being activated and the {here} package should be available:\n\n```\n* Project '~/project' loaded. [renv 0.12.0]\n\u003e library(here)\nhere() starts at /home/shiny/project\n```\n\n\n## Shiny with K means\n\nBased on a [demo app from the Shiny gallery](https://shiny.rstudio.com/gallery/kmeans-example.html) whose code is released under the MIT license at the time of writing in [this GitHub repository](https://github.com/rstudio/shiny-examples).\n\nThis app has no dependencies besides the [{shiny} package](https://cran.r-project.org/package=shiny).\nWhen based on a Docker image with the {shiny} package installed there is no need to install additional packages in the {renv} library.\nThis is accomplished by allowing an external library in {renv}'s settings.\n\nThe path to reconstruction is:\n\n1. Navigate to the `shiny_kmeans` folder.\n2. Build the \"install image\":\n\n```\ndocker build --build-arg R_VERSION=4.1.1 --build-arg SHINY_VERSION=1.5.17.973 --tag renv-test:latest -f Dockerfile_install .\n```\n\n3. Restore the project inside the container by running the `renv_install.sh` script.\n4. Build the final image:\n\n```\ndocker build --build-arg R_VERSION=4.1.1 --build-arg SHINY_VERSION=1.5.17.973 --tag renv-test:latest .\n```\n\nCheck out a running container with this command (where `3839` is an example port):\n\n```\ndocker run --rm -p 3839:3838 renv-test:latest\n```\n\nYou should see Shiny server starting. \nNavigate to \u003chttp://localhost:3839/project\u003e to see the Shiny app.\n\n\n## Shiny with K means in C++\n\nA Shiny app looking just like the first one, but using the [{ClusterR} package](https://cran.r-project.org/package=ClusterR) to perform K means clustering instead of the `kmeans` function from {stats}.\nThis illustrates how to utilize the packages already installed, {renv}'s cache and packages with compiled code having system requirements.\n\nIt can be tedious to find system requirements for packages.\nI know of two ways:\n\n* The [{remotes} package](https://remotes.r-lib.org) has the function `system_requirements`. Here is a [nice walkthrough](https://mdneuzerling.com/post/determining-system-dependencies-for-r-projects).\n* My own unofficial [{pkg.deps} package](https://github.com/robertdj/pkg.deps) that does the same as `system_requirements` without calling an RStudio Package Manager server. (Made before I became aware that {remotes} offers the same.)\n\nThe path to reconstruction is:\n\n1. Navigate to the `shiny_kmeans_rcpp` folder.\n2. Build the \"install image\":\n\n```\nDOCKER_BUILDKIT=1 docker build --build-arg R_VERSION=4.1.1 --build-arg SHINY_VERSION=1.5.17.973 --tag renv-test:latest -f Dockerfile_install .\n```\n\n3. Restore the project inside the container by running the `renv_install.sh` script.\n4. Build the final image:\n\n```\nDOCKER_BUILDKIT=1 docker build --build-arg R_VERSION=4.1.1 --build-arg SHINY_VERSION=4.1.1-1.5.17.973 --tag renv-test:latest .\n```\n\nCheck out a running container with this command (where `3839` is an example port):\n\n```\ndocker run --rm -p 3839:3838 renv-test:latest\n```\n\nYou should see Shiny server starting. \nNavigate to \u003chttp://localhost:3839/project\u003e to see the Shiny app.\n\n\n# Files on host\n\nNote that the `renv_install.sh` scripts modify files on the host.\n\nIt modifies {renv}'s cache as intended, but also the files in the project in order to isolate the project.\nIn particular, the file `renv/settings.dcf` is changed from something like\n\n```\nexternal.libraries:\nignored.packages:\npackage.dependency.fields: Imports, Depends, LinkingTo\nr.version:\nsnapshot.type: implicit\nuse.cache: TRUE\nvcs.ignore.library: TRUE\nvcs.ignore.local: TRUE\n```\n\nto\n\n```\nexternal.libraries: /usr/local/lib/R/site-library\nignored.packages:\npackage.dependency.fields: Imports, Depends, LinkingTo\nr.version:\nsnapshot.type: implicit\nuse.cache: TRUE\nvcs.ignore.library: TRUE\nvcs.ignore.local: TRUE\n```\n\nThese steps can be reverted by deleting the folder `renv/library` and reverting the changes in `renv/settings.dcf`.\nI think these steps are not just sufficient, but also necessary.\n\nThe path added in `external.libraries` is the normal package library in the current `FROM` image -- that is, the first element in the output of `.libPaths()`.\n\n\n# Private CRANs\n\nAt work I use a number of internal packages stored in a private CRAN that rely on authentication through HTTP (basic HTTP access with username/password in the URL or bearer authentication with a token in the header).\n\nThe approach here to install with a running container makes it easy to share these credentials as environment variables with e.g. a `-e` argument to a `docker run`.\n\nThis is very different from trying to install packages *at build time*, because it is difficult to make environment variables availabe in a *non-persistent manner* at image build time.\n\n","funding_links":[],"categories":["R"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertdj%2Frenv-docker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobertdj%2Frenv-docker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobertdj%2Frenv-docker/lists"}