The Easiest Method: Environment Variables
When working with pulling data from secure databases or other sources a developer might find themselves in a situation of needing to provide very sensitive information, such as a password or a token, in order to access the data that is needed or to successfully deploy a project. Handling those secrets in way that doesn’t expose them in the code directly is critical and where using environmental variable’s for securing sensitive variables is strongly recommended.
Additionally there may be parameters that are often needed that can be accessed as a variable more easily rather than having to type in every time.
For both of these cases knowing how environment variables can be leveraged can be very rewarding and it is surprising how little effort it can to take to set up.
Working with the .Renviron file
When R starts it loads a bunch of variables, settings, and configs for the user. This is really powerful and some of the magic for how it can work so apparently seamlessly.
However for power users we can leverage these behind the scenes config files so that we can include such things as variables in our project without including it in our code. The .Renviron file is the one most commonly interacted with for adding sensitive variables to a project in order to protect them from being exposed in the code.
With increased use of these behind the scenes config files and the growing direction of arranging code into projects there was the development of giving, on startup, having multiple options for each config file that can be loaded depending on what the user specifies. Broadly speaking there are two levels of config files:
- User
- Project
On startup, since R is trying to make things as seamless as possible for the user, it will use some logic to figure out which config to use. It will assume that if a project level config exists it should load that one (and not any others). If that project level config doesn’t exist, then it will default to the user level config. For more details on the different config files and the nuances see Managing R with .Rprofile, .Renviron, Rprofile.site, Renviron.site, rsession.conf, and repos.conf.
Just to re-iterate the key takeaway: When in doubt note that the project level file is given preference over user level config files. Only if the project level config file doesn’t exist will the user level file be sourced/pulled in.
There is a really excellent overview of R’s startup process here but in short it can be thought of this way:
Example with a user level .Renviron config file
usethis has a function for creating and editing the .Renviron file
library(usethis)
usethis::edit_r_environ()
Add the variables to that file in the format variable_name = "variable_value"
and save it. Restart the session so the new environment variables will be loaded with ctrl shift f10 or through the RStudio IDE
Saved variables can be accessed with:
variable_name <- Sys.getenv("variable_name")
When working in a more complex environment structure where separate project, site, and user environments are being support this support article has useful information with a deeper dive into R’s startup here.
Example with a project level .Renviron config file
Storing secrets securely can be done using the edit_r_environ function from the usethis package. For more overview see this overview.
Example:
library(usethis)
::edit_r_environ(scope = "project") usethis
Accessing those stored parameters later can be done using Sys.getenv("DB_NAME")
.
Be sure to add the project level .Renviron file to your .gitignore so you aren’t exposing secrets when code is being saved to your git repository. Similarly this can be done with the edit_git_ignore(scope = c("user", "project"))
function. For more best practices see securing credentials.
- While typically explicitly listing the file name is the desired addition, wildcards can be added to exclude a type of file. For example:
*.html
.
After updating these files the project should be closed and re-opened for any additions to be pulled in. One way to do this is through session -> restart R (ctrl-shift-f10).
Example with project level github secrets for environment variables
Another approach, particularly useful when automating testing and deployments using github actions, is to include the environment variables as secrets. Once this has been added through the git UI for the project they can then be referenced in the relevant deployment .yaml file with something like CONNECT_ENV_SET_ZD_USER: ${{ secrets.ZD_USER }}
. In the R scripts they will be referenced as usual with something like Sys.getenv("DB_NAME")
.
References for adding environment variables through the Connect UI
Starting with version 1.6, RStudio Connect allows Environment Variables. The variables are encrypted on-disk, and in-memory.
This can be done at the project level with securing deployment through the Connect UI.
Use Connect to manage environment variables through the UI: https://docs.posit.co/connect/user/content-settings/index.html#content-vars
Have your admin use a supervisor script to add Environment Variables automatically: https://docs.posit.co/connect/admin/process-management/index.html#example-environment-variables
Securing credentials solutions article: https://solutions.posit.co/connections/db/best-practices/managing-credentials/