Skip to contents
library(rix)

Introduction and installation

The goal of {rix} is to provide an easy way to generate default.nix files. These files are used by the Nix package manager to build an environment according to the instructions defined in it. Users can specify which version of R, R packages, other needed software, IDE etc should be available within that environment. Writing such files can be daunting for newcomers, and so {rix} provides a function called rix(), which takes care of the bulk of the work

{rix} does not require Nix to be installed to generate default.nix files, so you could generate the file on one machine, and then build and use the environment on another machine that has Nix installed on it (or on a CI/CD service like Github Actions for example). If you wish to fully take advantage of Nix, I suggest you use Determinate System’s installer. Nix can be installed on Linux, macOS and Windows (but on Windows WSL2 must be enabled).

On Linux, once Nix is instaled, all the software that will be installed will be saved to the /nix directory on the root partition. Complete development environments built with Nix can take up much space, so if the available space on your root parttion is limited, I advise you to mount the /nix folder on another partition with more space (for example, a secondary hard drive). For this, edit /etc/fstab and add the following line at the end:

/home/path_to/nix /nix none bind 0 0

This will map /nix to /home/path_to/nix which can be on a larger partition. If you have enough space on your root partition, you can ignore the above instructions.

Nix environments

An environment built by Nix is not totally isolated from the rest of the system. Suppose that you the program sl installed on your system, and suppose you build a Nix environment that also comes with sl. If you activate that environment, the version of sl that will run when called is the one included in the Nix environment. If, however, you start sl in a Nix environment that does not come with it, then your system’s sl will get used instead. This can be useful when working interactively with a Nix environment, because you can use your usual IDE to work with it. The only exception is RStudio: RStudio looks for R in predefined paths and cannot “see” the R provided by a Nix environment, it will instead use the version installed on your machine. This means that if you use RStudio to work interactively with R, you will need to install RStudio inside that environment. rix::rix() can generate a default.nix file that does that.

Day-to-day use of {rix}

The ideal workflow when using {rix} is to create a new, separate environment at the start of a project. Let’s say that you wish to analyse some data set, and need dplyr and ggplot2. Let’s also suppose that you use RStudio as your IDE. With the rix::rix() function, you can easily generate the right default.nix file. You need to provide the following inputs to rix():

  • r_ver: the version of R required. Use “latest” for the latest version;
  • r_pkgs: the required R packages. For example “dplyr”;
  • system_pkgs: the required system packages, if needed. For example “quarto”, or a Python interpreter;
  • git_pkgs: list of git packages to add. See the example below;
  • ide: the integrated development editor to use. For example “rstudio” if you want to use RStudio. Refer to the “Interactive work with {rix}” for more details;
  • path: the path where to save the default.nix file.
  • overwrite: whether to overwrite the default.nix file or not.
  • print: whether to print the default.nix file to the console or not.

In such a situation, you could create an environment with the following call to rix():

path_default_nix <- tempdir()

rix(r_ver = "latest",
    r_pkgs = c("dplyr", "ggplot2"),
    system_pkgs = NULL,
    git_pkgs = NULL,
    ide = "rstudio",
    project_path = path_default_nix,
    overwrite = TRUE,
    print = TRUE)
#> # This file was generated by the {rix} R package v0.5.1.9000 on 2024-01-24
#> # with following call:
#> # >rix(r_ver = "5f5210aa20e343b7e35f40c033000db0ef80d7b9",
#> #  > r_pkgs = c("dplyr",
#> #  > "ggplot2"),
#> #  > system_pkgs = NULL,
#> #  > git_pkgs = NULL,
#> #  > ide = "rstudio",
#> #  > project_path = path_default_nix,
#> #  > overwrite = TRUE,
#> #  > print = TRUE)
#> # It uses nixpkgs' revision 5f5210aa20e343b7e35f40c033000db0ef80d7b9 for reproducibility purposes
#> # which will install R version latest
#> # Report any issues to https://github.com/b-rodrigues/rix
#> let
#>  pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/5f5210aa20e343b7e35f40c033000db0ef80d7b9.tar.gz") {};
#>  rpkgs = builtins.attrValues {
#>   inherit (pkgs.rPackages) dplyr ggplot2;
#> };
#>    system_packages = builtins.attrValues {
#>   inherit (pkgs) R glibcLocales ;
#> };
#>  rstudio_pkgs = pkgs.rstudioWrapper.override {
#>   packages = [  rpkgs ];
#> };
#>  in
#>   pkgs.mkShell {
#>     LOCALE_ARCHIVE = if pkgs.system == "x86_64-linux" then  "${pkgs.glibcLocales}/lib/locale/locale-archive" else "";
#>     LANG = "en_US.UTF-8";
#>     LC_ALL = "en_US.UTF-8";
#>     LC_TIME = "en_US.UTF-8";
#>     LC_MONETARY = "en_US.UTF-8";
#>     LC_PAPER = "en_US.UTF-8";
#>     LC_MEASUREMENT = "en_US.UTF-8";
#> 
#>     buildInputs = [  rpkgs  system_packages rstudio_pkgs ];
#>       shellHook = "R --vanilla";
#>   }

To start using this environment, open a console in the folder containing default.nix and use the following Nix command:

nix-build

nix-build is a Nix command that builds an environment according to the specifications found in a default.nix file. Once the environment is done building, you should find a new file called result next to the default.nix file. This file is a symlink to the software installed by Nix. To now use the environment, type:

nix-shell

You can now start the RStudio provided by that environment by typing rstudio. This will start a version of RStudio specific to this environment. If you already had RStudio installed by using your operating system’s installer, that version of RStudio will not be able to interact with Nix environments. This is because RStudio looks for R in certain specific paths that don’t include any Nix environments. This is not the case with other editors like Visual Studio Code or Emacs. If you use Visual Studio Code, you can use the following call to rix():

path_default_nix <- tempdir()

rix(r_ver = "latest",
    r_pkgs = c("dplyr", "ggplot2"),
    system_pkgs = NULL,
    git_pkgs = NULL,
    ide = "code",
    project_path = path_default_nix,
    overwrite = TRUE)

(note the value provided to the ide argument).

This generates the following default.nix file:

#> # This file was generated by the {rix} R package v0.5.1.9000 on 2024-01-24
#> # with following call:
#> # >rix(r_ver = "5f5210aa20e343b7e35f40c033000db0ef80d7b9",
#> #  > r_pkgs = c("dplyr",
#> #  > "ggplot2"),
#> #  > system_pkgs = NULL,
#> #  > git_pkgs = NULL,
#> #  > ide = "code",
#> #  > project_path = path_default_nix,
#> #  > overwrite = TRUE)
#> # It uses nixpkgs' revision 5f5210aa20e343b7e35f40c033000db0ef80d7b9 for reproducibility purposes
#> # which will install R version latest
#> # Report any issues to https://github.com/b-rodrigues/rix
#> let
#>  pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/5f5210aa20e343b7e35f40c033000db0ef80d7b9.tar.gz") {};
#>  rpkgs = builtins.attrValues {
#>   inherit (pkgs.rPackages) dplyr ggplot2 languageserver;
#> };
#>    system_packages = builtins.attrValues {
#>   inherit (pkgs) R glibcLocales ;
#> };
#>   in
#>   pkgs.mkShell {
#>     LOCALE_ARCHIVE = if pkgs.system == "x86_64-linux" then  "${pkgs.glibcLocales}/lib/locale/locale-archive" else "";
#>     LANG = "en_US.UTF-8";
#>     LC_ALL = "en_US.UTF-8";
#>     LC_TIME = "en_US.UTF-8";
#>     LC_MONETARY = "en_US.UTF-8";
#>     LC_PAPER = "en_US.UTF-8";
#>     LC_MEASUREMENT = "en_US.UTF-8";
#> 
#>     buildInputs = [  rpkgs  system_packages  ];
#>       shellHook = "R --vanilla";
#>   }

As you can see, specifying ide = "code" adds the languageserver package to the list of packages that must be installed for this environment. This is because Visual Studio Code requires this package to interact with R. This will not install a Nix environment-specific version of Visual Studio Code. Now, instead of typing rstudio in the Nix shell of your environment, type code and this will start the Visual Studio Code you usually use.

If you use another editor, like Emacs, then use ide = "other", and start that editor inside an activated Nix environment.

For more details on interactive use, read the “Interactive use” vignette.

Running old projects with {rix}

The example below shows how to create a default.nix with instructions to build an environment with R version 4.2.1, the dplyr and janitor packages and no specific IDE:

path_default_nix <- tempdir()

rix(r_ver = "4.2.1",
    r_pkgs = c("dplyr", "janitor"),
    system_pkgs = c("quarto"),
    git_pkgs = NULL,
    ide = "other",
    project_path = path_default_nix,
    overwrite = TRUE)

The file looks like this:

#> # This file was generated by the {rix} R package v0.5.1.9000 on 2024-01-24
#> # with following call:
#> # >rix(r_ver = "79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a",
#> #  > r_pkgs = c("dplyr",
#> #  > "janitor"),
#> #  > system_pkgs = c("quarto"),
#> #  > git_pkgs = NULL,
#> #  > ide = "other",
#> #  > project_path = path_default_nix,
#> #  > overwrite = TRUE)
#> # It uses nixpkgs' revision 79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a for reproducibility purposes
#> # which will install R version 4.2.1
#> # Report any issues to https://github.com/b-rodrigues/rix
#> let
#>  pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a.tar.gz") {};
#>  rpkgs = builtins.attrValues {
#>   inherit (pkgs.rPackages) dplyr janitor;
#> };
#>    system_packages = builtins.attrValues {
#>   inherit (pkgs) R glibcLocales quarto;
#> };
#>   in
#>   pkgs.mkShell {
#>     LOCALE_ARCHIVE = if pkgs.system == "x86_64-linux" then  "${pkgs.glibcLocales}/lib/locale/locale-archive" else "";
#>     LANG = "en_US.UTF-8";
#>     LC_ALL = "en_US.UTF-8";
#>     LC_TIME = "en_US.UTF-8";
#>     LC_MONETARY = "en_US.UTF-8";
#>     LC_PAPER = "en_US.UTF-8";
#>     LC_MEASUREMENT = "en_US.UTF-8";
#> 
#>     buildInputs = [  rpkgs  system_packages  ];
#>       shellHook = "R --vanilla";
#>   }

The first line is quite important, as it shows which revision of nixpkgs is being used for this environment. The revision is the commit hash of that particular release of nixpkgs, here: 79b3d4bcae8. This revision of nixpkgs is the one that shipped version 4.2.1 of R, so the dplyr and janitor packages that will get installed will be the versions available in that revision as well. This means that R versions and package versions are always coupled when using Nix. However, if you need a specific version of R, but also a specific version of a package that is not available in that particular Nix revision, one solution is to install that package from Github.

Installing old packages archived on CRAN

It is also possible to install an arbitrary version of a package that has been archived on CRAN:

rix(r_ver = "4.2.1",
    r_pkgs = c("dplyr@0.8.0", "janitor@1.0.0"),
    system_pkgs = NULL,
    git_pkgs = NULL,
    ide = "other",
    project_path = path_default_nix,
    overwrite = TRUE)
cat(readLines(paste0(path_default_nix, "/default.nix")), sep = "\n")
#> # This file was generated by the {rix} R package v0.5.1.9000 on 2024-01-24
#> # with following call:
#> # >rix(r_ver = "79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a",
#> #  > r_pkgs = c("dplyr@0.8.0",
#> #  > "janitor@1.0.0"),
#> #  > system_pkgs = NULL,
#> #  > git_pkgs = NULL,
#> #  > ide = "other",
#> #  > project_path = path_default_nix,
#> #  > overwrite = TRUE)
#> # It uses nixpkgs' revision 79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a for reproducibility purposes
#> # which will install R version 4.2.1
#> # Report any issues to https://github.com/b-rodrigues/rix
#> let
#>  pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a.tar.gz") {};
#>   git_archive_pkgs = [ (pkgs.rPackages.buildRPackage {
#>     name = "dplyr";
#>     src = pkgs.fetchzip {
#>       url = "https://cran.r-project.org/src/contrib/Archive/dplyr/dplyr_0.8.0.tar.gz";
#>       sha256 = "sha256-f30raalLd9KoZKZSxeTN71PG6BczXRIiP6g7EZeH09U=";
#>     };
#>     propagatedBuildInputs = builtins.attrValues {
#>       inherit (pkgs.rPackages) assertthat glue magrittr pkgconfig R6 Rcpp rlang tibble tidyselect BH plogr;
#>     };
#>   })
#> (pkgs.rPackages.buildRPackage {
#>     name = "janitor";
#>     src = pkgs.fetchzip {
#>       url = "https://cran.r-project.org/src/contrib/Archive/janitor/janitor_1.0.0.tar.gz";
#>       sha256 = "sha256-3NJomE/CNbOZ+ohuVZJWe2n1RVGUm8x8a0A0qzLmdN4=";
#>     };
#>     propagatedBuildInputs = builtins.attrValues {
#>       inherit (pkgs.rPackages) dplyr tidyr snakecase magrittr purrr rlang;
#>     };
#>   })];
#>   system_packages = builtins.attrValues {
#>   inherit (pkgs) R glibcLocales ;
#> };
#>   in
#>   pkgs.mkShell {
#>     LOCALE_ARCHIVE = if pkgs.system == "x86_64-linux" then  "${pkgs.glibcLocales}/lib/locale/locale-archive" else "";
#>     LANG = "en_US.UTF-8";
#>     LC_ALL = "en_US.UTF-8";
#>     LC_TIME = "en_US.UTF-8";
#>     LC_MONETARY = "en_US.UTF-8";
#>     LC_PAPER = "en_US.UTF-8";
#>     LC_MEASUREMENT = "en_US.UTF-8";
#> 
#>     buildInputs = [ git_archive_pkgs   system_packages  ];
#>       shellHook = "R --vanilla";
#>   }

This feature should ideally be used sparingly. If you want to reconstruct an environment as it was around 2019, use the version of R that was current at the time. This will ensure that every package that gets installed is at a version compatible with that version of R, which might not be the case if you need to install a very old version of one particular package.

Installing packages from Github

It is also possible to install packages from Github:

rix(r_ver = "4.2.1",
    r_pkgs = c("dplyr", "janitor"),
    system_pkgs = c("quarto"),
    git_pkgs = list(
                 list(package_name = "housing",
                   repo_url = "https://github.com/rap4all/housing/",
                   branch_name = "fusen",
                   commit = "1c860959310b80e67c41f7bbdc3e84cef00df18e"),
                 list(package_name = "fusen",
                   repo_url = "https://github.com/ThinkR-open/fusen",
                   branch_name = "main",
                   commit = "d617172447d2947efb20ad6a4463742b8a5d79dc")
    ),
    ide = "other",
    project_path = path_default_nix,
    overwrite = TRUE)
cat(readLines(paste0(path_default_nix, "/default.nix")), sep = "\n")
#> # This file was generated by the {rix} R package v0.5.1.9000 on 2024-01-24
#> # with following call:
#> # >rix(r_ver = "79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a",
#> #  > r_pkgs = c("dplyr",
#> #  > "janitor"),
#> #  > system_pkgs = c("quarto"),
#> #  > git_pkgs = list(list(package_name = "housing",
#> #  > repo_url = "https://github.com/rap4all/housing/",
#> #  > branch_name = "fusen",
#> #  > commit = "1c860959310b80e67c41f7bbdc3e84cef00df18e"),
#> #  > list(package_name = "fusen",
#> #  > repo_url = "https://github.com/ThinkR-open/fusen",
#> #  > branch_name = "main",
#> #  > commit = "d617172447d2947efb20ad6a4463742b8a5d79dc")),
#> #  > ide = "other",
#> #  > project_path = path_default_nix,
#> #  > overwrite = TRUE)
#> # It uses nixpkgs' revision 79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a for reproducibility purposes
#> # which will install R version 4.2.1
#> # Report any issues to https://github.com/b-rodrigues/rix
#> let
#>  pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a.tar.gz") {};
#>  rpkgs = builtins.attrValues {
#>   inherit (pkgs.rPackages) dplyr janitor;
#> };
#>  git_archive_pkgs = [(pkgs.rPackages.buildRPackage {
#>     name = "housing";
#>     src = pkgs.fetchgit {
#>       url = "https://github.com/rap4all/housing/";
#>       branchName = "fusen";
#>       rev = "1c860959310b80e67c41f7bbdc3e84cef00df18e";
#>       sha256 = "sha256-s4KGtfKQ7hL0sfDhGb4BpBpspfefBN6hf+XlslqyEn4=";
#>     };
#>     propagatedBuildInputs = builtins.attrValues {
#>       inherit (pkgs.rPackages) dplyr ggplot2 janitor purrr readxl rlang rvest stringr tidyr;
#>     };
#>   })
#> (pkgs.rPackages.buildRPackage {
#>     name = "fusen";
#>     src = pkgs.fetchgit {
#>       url = "https://github.com/ThinkR-open/fusen";
#>       branchName = "main";
#>       rev = "d617172447d2947efb20ad6a4463742b8a5d79dc";
#>       sha256 = "sha256-TOHA1ymLUSgZMYIA1a2yvuv0799svaDOl3zOhNRxcmw=";
#>     };
#>     propagatedBuildInputs = builtins.attrValues {
#>       inherit (pkgs.rPackages) attachment cli desc devtools glue here magrittr parsermd roxygen2 stringi tibble tidyr usethis yaml;
#>     };
#>   }) ];
#>   system_packages = builtins.attrValues {
#>   inherit (pkgs) R glibcLocales quarto;
#> };
#>   in
#>   pkgs.mkShell {
#>     LOCALE_ARCHIVE = if pkgs.system == "x86_64-linux" then  "${pkgs.glibcLocales}/lib/locale/locale-archive" else "";
#>     LANG = "en_US.UTF-8";
#>     LC_ALL = "en_US.UTF-8";
#>     LC_TIME = "en_US.UTF-8";
#>     LC_MONETARY = "en_US.UTF-8";
#>     LC_PAPER = "en_US.UTF-8";
#>     LC_MEASUREMENT = "en_US.UTF-8";
#> 
#>     buildInputs = [ git_archive_pkgs rpkgs  system_packages  ];
#>       shellHook = "R --vanilla";
#>   }

This will install two packages from Github: the {housing} package and more specifically the code as it is in the fusen branch. The commit is also provided, to pin the exact version of the package needed. The fusen package is also installed, from the main branch at commit d617172447d.

A complete example

This example shows how all features of {rix} can work together:

rix(r_ver = "4.2.1",
    r_pkgs = c("dplyr", "janitor", "AER@1.2-8"),
    system_pkgs = c("quarto"),
    git_pkgs = list(
                 list(package_name = "housing",
                   repo_url = "https://github.com/rap4all/housing/",
                   branch_name = "fusen",
                   commit = "1c860959310b80e67c41f7bbdc3e84cef00df18e"),
                 list(package_name = "fusen",
                   repo_url = "https://github.com/ThinkR-open/fusen",
                   branch_name = "main",
                   commit = "d617172447d2947efb20ad6a4463742b8a5d79dc")
    ),
    ide = "rstudio",
    project_path = path_default_nix,
    overwrite = TRUE)
cat(readLines(paste0(path_default_nix, "/default.nix")), sep = "\n")
#> # This file was generated by the {rix} R package v0.5.1.9000 on 2024-01-24
#> # with following call:
#> # >rix(r_ver = "79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a",
#> #  > r_pkgs = c("dplyr",
#> #  > "janitor",
#> #  > "AER@1.2-8"),
#> #  > system_pkgs = c("quarto"),
#> #  > git_pkgs = list(list(package_name = "housing",
#> #  > repo_url = "https://github.com/rap4all/housing/",
#> #  > branch_name = "fusen",
#> #  > commit = "1c860959310b80e67c41f7bbdc3e84cef00df18e"),
#> #  > list(package_name = "fusen",
#> #  > repo_url = "https://github.com/ThinkR-open/fusen",
#> #  > branch_name = "main",
#> #  > commit = "d617172447d2947efb20ad6a4463742b8a5d79dc")),
#> #  > ide = "rstudio",
#> #  > project_path = path_default_nix,
#> #  >      overwrite = TRUE)
#> # It uses nixpkgs' revision 79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a for reproducibility purposes
#> # which will install R version 4.2.1
#> # Report any issues to https://github.com/b-rodrigues/rix
#> let
#>  pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a.tar.gz") {};
#>  rpkgs = builtins.attrValues {
#>   inherit (pkgs.rPackages) dplyr janitor;
#> };
#>  git_archive_pkgs = [(pkgs.rPackages.buildRPackage {
#>     name = "housing";
#>     src = pkgs.fetchgit {
#>       url = "https://github.com/rap4all/housing/";
#>       branchName = "fusen";
#>       rev = "1c860959310b80e67c41f7bbdc3e84cef00df18e";
#>       sha256 = "sha256-s4KGtfKQ7hL0sfDhGb4BpBpspfefBN6hf+XlslqyEn4=";
#>     };
#>     propagatedBuildInputs = builtins.attrValues {
#>       inherit (pkgs.rPackages) dplyr ggplot2 janitor purrr readxl rlang rvest stringr tidyr;
#>     };
#>   })
#> (pkgs.rPackages.buildRPackage {
#>     name = "fusen";
#>     src = pkgs.fetchgit {
#>       url = "https://github.com/ThinkR-open/fusen";
#>       branchName = "main";
#>       rev = "d617172447d2947efb20ad6a4463742b8a5d79dc";
#>       sha256 = "sha256-TOHA1ymLUSgZMYIA1a2yvuv0799svaDOl3zOhNRxcmw=";
#>     };
#>     propagatedBuildInputs = builtins.attrValues {
#>       inherit (pkgs.rPackages) attachment cli desc devtools glue here magrittr parsermd roxygen2 stringi tibble tidyr usethis yaml;
#>     };
#>   }) (pkgs.rPackages.buildRPackage {
#>     name = "AER";
#>     src = pkgs.fetchzip {
#>       url = "https://cran.r-project.org/src/contrib/Archive/AER/AER_1.2-8.tar.gz";
#>       sha256 = "sha256-OqxXcnUX/2C6wfD5fuNayc8OU+mstI3tt4eBVGQZ2S0=";
#>     };
#>     propagatedBuildInputs = builtins.attrValues {
#>       inherit (pkgs.rPackages) car lmtest sandwich survival zoo Formula;
#>     };
#>   })];
#>   system_packages = builtins.attrValues {
#>   inherit (pkgs) R glibcLocales quarto;
#> };
#>  rstudio_pkgs = pkgs.rstudioWrapper.override {
#>   packages = [ git_archive_pkgs rpkgs ];
#> };
#>  in
#>   pkgs.mkShell {
#>     LOCALE_ARCHIVE = if pkgs.system == "x86_64-linux" then  "${pkgs.glibcLocales}/lib/locale/locale-archive" else "";
#>     LANG = "en_US.UTF-8";
#>     LC_ALL = "en_US.UTF-8";
#>     LC_TIME = "en_US.UTF-8";
#>     LC_MONETARY = "en_US.UTF-8";
#>     LC_PAPER = "en_US.UTF-8";
#>     LC_MEASUREMENT = "en_US.UTF-8";
#> 
#>     buildInputs = [ git_archive_pkgs rpkgs  system_packages rstudio_pkgs ];
#>       shellHook = "R --vanilla";
#>   }

To learn more about using {rix} on a daily basis, read the Interactive use vignette.