R in Windows Containers

There are numerous Dockerfiles/Docker images available with R running on a Linux distribution. Here I look into running R in a Windows container.

I see two advantages:

R in Server Core

CRAN has an archive with installers for the last 16 years of R for Windows: https://cran.r-project.org/bin/windows/base/old.

Just as in a Linux container we only have a command line/PowerShell available in a Windows container, so we have to install R from the command line.

For many practical purposes, Rtools are also necessary on Windows. The Rtools installer also has some special flags which I found in the CI script used in the daily build of R for Windows

Armed with these commands R and Rtools can be installed as in this Dockerfile:

FROM mcr.microsoft.com/windows/servercore:ltsc2019

ARG R_VERSION
ENV R_VERSION=${R_VERSION}

RUN curl.exe -o R-%R_VERSION%-win.exe https://cran.r-project.org/bin/windows/base/old/%R_VERSION%/R-%R_VERSION%-win.exe

RUN R-%R_VERSION%-win.exe /VERYSILENT /DIR="C:\R\"

RUN curl.exe -o Rtools.exe https://cran.r-project.org/bin/windows/Rtools/Rtools35.exe

RUN Rtools.exe /TYPE=full /VERYSILENT -NoNewWindow -Wait

CMD [ "C:\R\bin\R.exe", "--vanilla" ]

The R version is passed as a build argument:

docker build --build-arg R_VERSION=3.6.0 --tag r-base:3.6.0 .

Multi-stage build

In the first version the two downloaded installers (R-3.6.0-win.exe and Rtools.exe) are left in the final container. They can be removed by either downloading and installing in one the same RUN or by using a multi-stage build where the installed programs are copied into a fresh image:

FROM mcr.microsoft.com/windows/servercore:ltsc2019 as builder

ARG R_VERSION=3.6.0
ENV R_VERSION=${R_VERSION}

RUN curl.exe -o R-%R_VERSION%-win.exe https://cran.r-project.org/bin/windows/base/old/%R_VERSION%/R-%R_VERSION%-win.exe

RUN R-%R_VERSION%-win.exe /VERYSILENT /DIR="C:\R\"

RUN curl.exe -o Rtools.exe https://cran.r-project.org/bin/windows/Rtools/Rtools35.exe

RUN Rtools.exe /TYPE=full /VERYSILENT -NoNewWindow -Wait


# --------------------------------------------------------------------

FROM mcr.microsoft.com/windows/servercore:ltsc2019

COPY --from=builder C:\\R C:\\R

COPY --from=builder C:\\Rtools C:\\Rtools

RUN setx path "%path%;C:\R\bin;C:\Rtools\bin"

RUN echo options(repos = "https://mran.microsoft.com/snapshot/2019-07-04") >> C:\R\library\base\R\Rprofile

CMD [ "R.exe", "--vanilla" ]

In the final image we also set the path to include the binaries of R and Rtools. Inside the container, the path can be printed with the command echo %path%.

I also specify a CRAN that is compatible with this version of R – just as I do with every R installation.

Image sizes

A key difference between Windows Docker images and Linux Docker images is their size:

PS > docker image ls
REPOSITORY                             TAG        SIZE
r-base                                 3.6.0      5.8GB
mcr.microsoft.com/windows/servercore   ltsc2019   4.71GB
mcr.microsoft.com/windows/nanoserver   1809       250MB

The servercore image is huge compared to any Linux distribution’s plain vanilla image. R and Rtools also take up more than a Gigabyte, which is also quite a lot more than on Linux.

However, the nanoserver image is quite small, so it’s tempting to use this instead of servercore.

R in Nano Server

TL; DR: I cannot make R run in the nanoserver image. This section is to document my attemps.

Simply replacing servercore with nanoserver in the above Dockerfile does not work – the installer cannot run.

Instead we can try out a multi-stage build where R is installed in a servercore image and then copied to a nanoserver image. Running a container based on such an image will exit immediately with error code 3221225781. To see the error code, we can run the container without the --rm flag and list the containers:

PS > docker run r-base-nano:3.6.0
PS > docker container ls -a
IMAGE               COMMAND         STATUS
r-base-nano:3.6.0   "R.exe --va…"   Exited (3221226625)

Runtime dependencies

It turns out that R has a number of runtime dependencies on Windows. These can be revealed with the program Process Explorer in a normal Windows. Process Explorer lists all dll’s that a program depends on.

On my computer I see the following for R when it is started from the command line.

Process Explorer

(As noted in a previous post starting an R instance on Windows actually starts two R.exe and one Rterm.exe.)

All non-R related dll’s for Rterm.exe and the two R.exe are in C:\Windows\System32.

My final attempt is to copy the dll’s from servercore that are missing in nanoserver:

FROM mcr.microsoft.com/windows/servercore:ltsc2019 as builder

ARG R_VERSION
ENV R_VERSION=${R_VERSION}

RUN curl.exe -o R-%R_VERSION%-win.exe https://cran.r-project.org/bin/windows/base/old/%R_VERSION%/R-%R_VERSION%-win.exe

RUN R-%R_VERSION%-win.exe /VERYSILENT /DIR="C:\R\"

RUN curl.exe -o Rtools.exe https://cran.r-project.org/bin/windows/Rtools/Rtools35.exe

RUN Rtools.exe /TYPE=full /VERYSILENT -NoNewWindow -Wait


# --------------------------------------------------------------------

FROM mcr.microsoft.com/windows/nanoserver:1809

COPY --from=builder C:\\R C:\\R

COPY --from=builder C:\\Rtools C:\\Rtools

RUN setx path "%path%;C:\R\bin;C:\Rtools\bin"

RUN echo options(repos = "https://mran.microsoft.com/snapshot/2019-07-04") >> C:\R\library\base\R\Rprofile

# System runtime dependencies
COPY --from=builder \
    C:\\Windows\\System32\\advapi32.dll \
    C:\\Windows\\System32\\clbcatq.dll \
    C:\\Windows\\System32\\comdlg32.dll \
    C:\\Windows\\System32\\CoreMessaging.dll \
    C:\\Windows\\System32\\gdi32.dll \
    C:\\Windows\\System32\\gdi32full.dll \
    C:\\Windows\\System32\\imm32.dll \
    C:\\Windows\\System32\\kernel.appcore.dll \
    C:\\Windows\\System32\\msimg32.dll \
    C:\\Windows\\System32\\ole32.dll \
    C:\\Windows\\System32\\shell32.dll \
    C:\\Windows\\System32\\shlwapi.dll \
    C:\\Windows\\System32\\user32.dll \
    C:\\Windows\\System32\\uxtheme.dll \
    C:\\Windows\\System32\\version.dll \
    C:\\Windows\\System32\\win32u.dll \
    C:\\Windows\\System32\\windows.storage.dll \
    C:\\Windows\\System32\\winmm.dll \
    C:\\Windows\\System32\\winmmbase.dll \
    C:\\Windows\\System32\\kernel32.dll \
    C:\\Windows\\System32/

CMD [ "R.exe", "--vanilla" ]

The R in the resulting image also crashes immediately, but with a diffrent error code (3221226625).