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:
|
|
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 Processes
The 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:
|
|
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 askssuto keep the current environment inherited from the caller (which includes Docker’sENVvariables).
Because Linux’s su utility cannot honor both conflicting behaviors, it chooses --login and prints a warning:
|
|
Note: 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.
Solutions
Since 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:
|
|
When 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
exportkeywords or$variable expansions. - Uses simple
KEY=valuekey-value pairs.
In your Dockerfile, set up /home/shiny/.Renviron:
|
|
In your Shiny app (app.R or server.R), you can access it reliably:
|
|
Summary
- The Problem: Shiny Server calls
su shiny --login --preserve-environment, causingsuto discard inherited environment variables (such as DockerENV) in favor of a clean login shell. - Solution 1 (
~/.profile): Appendexport KEY="value"to/home/shiny/.profilein yourDockerfile. - Solution 2 (
~/.Renviron): AppendKEY="value"to/home/shiny/.Renvironin yourDockerfile.
Both approaches integrate seamlessly with Docker builds and ensure your Shiny application receives all required configuration variables cleanly.
Last modified on 2026-08-15