When I am working on, well, work-related work, I use my company-issued laptop with Windows, Microsoft Office tools, and an installation of RStudio Desktop.
Recently I have been wanting to setup a personal installation of RStudio on my home file server as well. The main reasons being that RStudio makes for a great general-purpose IDE, and I’ve been wanting to start working with Quarto on some things, so right now seemed like a great time to figure it out.
Backstory
I wrote in a previous post that I am working on making my home file server more stable, which means moving more of the userland packages to containers. Most of the programs I use on that PC (Firefox, VLC, Gimp, etc.) all have flatpaks that work quite well for me.
However, R and RStudio are not in that category. On Debian, both require going outside of the official Debian repositories: R has a repository for up-to-date versions, while RStudio does not (although they do provide binary downloads). Add to that the system dependencies of some packages and now there are random libraries installed all over, with no dependency chain in apt. There is the option to use the Debian packages from the CRAN repository, but those are not complete and have given me issues in the past, as far as permissions, updates, etc.
It seemed to me that R would need it’s own environment. How best to do that? I turned to Docker for this particular case.
Docker Setup
Base Image
Luckily, smarter people than myself have already thought about this and started the Rocker Project. They have many different containers all built in layers on each other so it’s easy to find a good starting point for running R/shiny apps, or building a general dev environment like I was looking for.
Adding on Layers
At the time of this post I chose the rocker/tidyverse
image to build my RStudio environment, but this could change in the future (see my repo for the latest).1 Having Rstudio server and the tidyverse packages gets me 90% of the way to where I want to be, but there are other packages that I use quite a bit. To get those packages I would either need to
- Option 1:
- Spin up an image,
- Download the packages in the image
- Save that modified environment as a new image via
docker commit
- Option 2:
- Create a Dockerfile and let
docker build
handle it
- Create a Dockerfile and let
I am a big fan of keeping things simple, reproducible, and mostly in line with the intended workflow (sometimes rules are made to be broken, but I am not familiar enough with docker to be getting my hands that dirty just yet), so I went with Option 2.
Configuration
At the time of this post, this is my Dockerfile:
FROM rocker/tidyverse:4.2.1
COPY packages.R /home/rstudio/packages.R
RUN R -q -e "source('/home/rstudio/packages.R')" \
&& rm -rf /tmp/* \
&& strip /usr/local/lib/R/site-library/*/libs/*.so
I start with the rocker/tidyverse:4.2.1
image, then copy an R script into the rstudio home directory:
install.packages(
c(
"markdown",
"gt",
"DT",
"kableExtra",
"flextable",
"huxtable",
"reactable",
"formattable",
"pixiedust",
"agricolae",
"car"
) )
After the script is run, I execute 2 more commands to clean up the image (as advised by the Rocker team). This image is available on Docker Hub as tclark89/tidyverse-extra
One day I may dig more deeply into setting up my own very custom image by building something more from scratch, but for now the rocker/tidyverse
image works as great jumping-off point.
Compose
Now that the image has been created it needs to be spun up, and it needs to be run with certain parameters. The best way to do that is with a docker-compose.yml
file. Currently mine looks like this:
services:
rstudio:
image: tclark89/tidyverse-extra:4.2.1
ports:
- "8787:8787"
environment:
PASSWORD: rstudio
ROOT: true
volumes:
- ~/.config/rstudio:/home/rstudio/.config/rstudio
- ~/.local/share/rstudio:/home/rstudio/.local/share/rstudio
- ~/code/R:/home/rstudio/workspace
The docker-compose.yml
file starts the rstudio
service:
Uses my custom docker image as the base
Maps the container ports
Sets the rstudio user’s password and gives it sudo via environment variables
Maps some directories to be shared between host and container. This lets settings and files persist in my
/home
directory between sessions.
The rocker team provides an example compose file as well.
Running the Container
All it takes to run the docker container now is to cd
to the directory with the docker-compose.yml file and issue the command: docker compose up
. However, I would like for this to happen automatically so I can just login and go. There may be a different way to do this, but I went with a systemd service file.
After some digging through the Arch Wiki and various SO posts I settled on the following:
[Unit]
Description=%i service with docker compose
PartOf=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=true
WorkingDirectory=/home/%u/docker/compose/%i
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker-compose down
[Install]
WantedBy=multi-user.target
This service file is a user @.service
, so the file is named docker-compose@.service
and called via:
systemctl start --user docker-compose@tidyverse-extra.service
or for a persistent setup:
systemctl enable --now --user docker-compose@tidyverse-extra.service
Because docker compose
looks for a docker-compose.yml
file in the working directory, I needed to specify one in this file and make sure that directory existed in my /home
. The %u
makes this file user-agnostic and the %i
and @
make it usable for any other docker service I may decide to use later. I just need to make sure that I create a directory in ~/docker/compose/
and put the docker-compose.yml
file there.
As a final note, this will only work if your user is part of the docker group, otherwise docker needs root privileges. This file could just as easily be made to work as root, but the docker-compose.yml file would need to have absolute paths instead of user-relative ones.
Update: 11/7/2022
Docker doesn’t recommend making images persistent with systemd files. There is a built-in method for starting containers at boot:
- Start the container with
docker run
,docker compose up
, etc. - Run the command:
docker update --restart unless-stopped $container_name
Much simpler!
Using RStudio
Now that all of the pieces are in place, all I have to do to access RStudio at home is to open a browser, access my file server at port 8787, and log in to RStudio Server. Then I can just git pull
and get back to work on this website or whatever project I am focused on.
For now this all works wonderfully. I don’t have to muck around with dependencies on my server, and saving the docker files to GitHub and the image to Docker Hub means that it should be reproducible in case I can’t access the server. For instance, if I am away from home but want to work on a project I can spin up the docker image directly on my Chromebook (via the built-in Linux VM) and fire away. In fact, you can even run this image in a browser via Play with Docker, though it eats up quite a bit of RAM to do so.