[R] Environment Variables in R Shiny-Server Container: Problem and Solutions

Wait 5 sec.

[This article was first published on R on Zhenguo Zhang's Blog, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.Zhenguo Zhang’s Blog https://fortune9.netlify.app/2026/08/15/r-environment-variables-in-r-shiny-server-container/ –When dockerizing an R Shiny application hosted via Shiny Server (built from shiny server docker image https://hub.docker.com/r/rocker/shiny), a common issue developers face is that environment variables set via ENV instructions in the Dockerfile (or passed at runtime via docker run -e) are completely missing inside the R Shiny app.For example, if you set the following environment variable in your Dockerfile:1ENV SHOULD_IN_SHINY="From dockerfile"When your app launches and runs Sys.getenv("SHOULD_IN_SHINY"), it returns an empty string "" instead of "From dockerfile".This is not a problem with Docker itself, but rather a consequence of how Shiny Server spawns R worker processes.In this post, we will look into the root cause behind this behavior in Shiny Server and demonstrate the two recommended solutions to correctly expose environment variables to your R Shiny workers.The Root Cause: How Shiny Server Spawns R ProcessesThe reason environment variables do not carry over to your R session lies in how Shiny Server executes R worker processes inside the container.Shiny Server runs as a system service (typically as root or shiny). When launching an app instance, it re-executes R as the shiny unprivileged user using su. Specifically, the execution call combines two mutually exclusive flags:1su shiny --login --preserve-environment -c "... R ..."Let’s break down what these flags request from su:--login (-l): Starts a login shell. This intentionally resets the environment to a minimal whitelist (HOME, PATH, USER, TERM, etc.) and sources system startup profiles (/etc/profile, ~/.profile).--preserve-environment (-p): Explicitly asks su to keep the current environment inherited from the caller (which includes Docker’s ENV variables).Because Linux’s su utility cannot honor both conflicting behaviors, it chooses --login and prints a warning:1su: ignoring --preserve-environment, it's mutually exclusive with --loginNote: This warning is emitted by Shiny Server’s underlying process execution call, not by the R app itself. Attempting to suppress or patch it requires modifying and rebuilding Shiny Server C++ code, which is rarely practical.Because the login shell wins, all custom environment variables passed to the container via ENV or docker run are wiped before R ever starts.SolutionsSince patching Shiny Server is unnecessary, we can utilize the natural extension points provided by the login shell or R itself.Solution 1: Use ~/.profile (Recommended for User-Level Shell Vars)Since --login causes the shell to source ~/.profile for the shiny user, we can write our environment variables to /home/shiny/.profile during the Docker build phase.In your Dockerfile:12# Append environment variable to the shiny user's profileRUN echo 'export SHOULD_IN_SHINY="From dockerfile"' >> /home/shiny/.profileWhen Shiny Server executes su shiny --login ..., the login shell will read /home/shiny/.profile and load SHOULD_IN_SHINY into the environment right before starting the R process.Solution 2: Use ~/.Renviron (Recommended for R-Specific Configs)Alternatively, R automatically inspects and loads ~/.Renviron on startup, right after the shell environment is initialized.~/.Renviron is purpose-built for R:Scoped strictly to R processes.Does not use shell export keywords or $ variable expansions.Uses simple KEY=value key-value pairs.In your Dockerfile, set up /home/shiny/.Renviron:123# Set up .Renviron for the shiny userRUN echo 'SHOULD_IN_SHINY="From dockerfile"' >> /home/shiny/.Renviron \ && chown shiny:shiny /home/shiny/.RenvironIn your Shiny app (app.R or server.R), you can access it reliably:12should_in_shiny