A new blog post of the “Quarto Q&A” series.
This time, I will show how to publish your Quarto project as a Docker container as light as possible.
1 The Question/Problem
There are different ways to publish a Quarto project, either if it is a document, a book or a website:
But, what if you want to publish your Quarto project as a Docker container, as an app?
2 The Answer/Solution
Again, there are several ways to do it. I will show you one way to publish your Quarto project as a Docker container, as light as possible. To do so, I will use a Dockerfile with staged builds and a Quarto website project.
2.1 Create a project
First, I create a website using the Quarto CLI and add a code cell to the about.qmd
file that uses the palmerpenguins
I will publish this Quarto project as a Docker container.
quarto create-project --type website:blog mywebsite
cd mywebsite
echo '```{r}
library(ggplot2)
library(palmerpenguins)
ggplot(penguins) +
aes(x = bill_length_mm, y = bill_depth_mm) +
geom_point(aes(colour = species)) +
geom_smooth(method = "lm", se = FALSE)
```' >> about.qmd
2.2 renv
See https://rstudio.github.io/renv/.
2.2.1 Setup renv
I am using renv
to record the dependencies of the project1.
echo 'library(knitr)
library(rmarkdown)
library(ggplot2)
library(palmerpenguins)' >> _dependencies.R
Rscript -e 'install.packages("renv")' -e "renv::init()"
Rscript -e "renv::hydrate()" -e "renv::snapshot()"
2.2.2 The Dockerfile
Here, I am using “build arguments” (i.e., ARGS
) to specify the Quarto, the rig and the
This allows to change the versions without having to change the Dockerfile itself.
ARG QUARTO_VERSION="1.3.340"
FROM ghcr.io/quarto-dev/quarto:${QUARTO_VERSION} AS builder
ARG RIG_VERSION="latest"
ARG R_VERSION="release"
COPY install-rig.sh /tmp/install-rig.sh
RUN bash /tmp/install-rig.sh "${RIG_VERSION}"
RUN rig add ${R_VERSION} && Rscript -e 'pak::pkg_install("renv")'
COPY mywebsite /app
WORKDIR /app
RUN Rscript -e "renv::restore()"
RUN quarto render .
FROM httpd:alpine
COPY --from=builder /app/_site/ /usr/local/apache2/htdocs/
- 1
-
The base Docker image used as the “builder”, i.e., the image with all requirements to build your Quarto project. Here it is the Quarto image which is an Ubuntu based image with Quarto pre-installed.
The “builder” stage is used to install the specifiedrig
andversion, and to render the website. - 2
-
The
install-rig.sh
script is used to install therig
software. - 3
-
Using
rig
, we add the specifiedversion and install the renv
package. - 4
-
Copy the Quarto project into the
/app
directory of the “builder”. - 5
-
Restore the
lockfile
created withpak
and render the website with Quarto CLI. - 6
-
The second and last stage is based on the
httpd:alpine
image which is a light image with Apache pre-installed. - 7
-
Copy the rendered website from the “builder” to the
/usr/local/apache2/htdocs/
directory of the second stage image.
2.3 pak
2.3.1 Setup pak
Alternatively to renv
, it’s also possible de record (and restore) the dependencies of a project2.
Rscript -e 'install.packages("pak", repos = sprintf("https://r-lib.github.io/p/pak/stable/%s/%s/%s", .Platform$pkgType, R.Version()$os, R.Version()$arch))'
Rscript -e 'pak::lockfile_create(c("knitr", "rmarkdown", "ggplot2", "palmerpenguins"))'
2.3.2 The Dockerfile
Again, I am using “build arguments” (i.e., ARGS
) to specify the Quarto, the rig and the
This allows to change the versions without having to change the Dockerfile.
ARG QUARTO_VERSION="1.3.340"
FROM ghcr.io/quarto-dev/quarto:${QUARTO_VERSION} AS builder
ARG RIG_VERSION="latest"
ARG R_VERSION="release"
COPY install-rig.sh /tmp/install-rig.sh
RUN bash /tmp/install-rig.sh "${RIG_VERSION}"
RUN rig add ${R_VERSION}
COPY mywebsite /app
WORKDIR /app
RUN Rscript -e "pak::lockfile_install()" && quarto render .
FROM httpd:alpine
COPY --from=builder /app/_site/ /usr/local/apache2/htdocs/
- 1
-
The base Docker image used as the “builder”, i.e., the image with all requirements to build your Quarto project. Here it is the Quarto image which is an Ubuntu based image with Quarto pre-installed.
The “builder” stage is used to install the specifiedrig
andversion, and to render the website. - 2
-
The
install-rig.sh
script is used to install therig
software. - 3
-
Using
rig
, we add the specifiedversion which already includes pak
. - 4
-
Copy the Quarto project into the
/app
directory of the “builder”. - 5
-
Restore the
lockfile
created withpak
and render the website with Quarto CLI. - 6
-
The second and last stage is based on the
httpd:alpine
image which is a light image with Apache pre-installed. - 7
-
Copy the rendered website from the “builder” to the
/usr/local/apache2/htdocs/
directory of the second stage image.
2.4 Build the image
It’s time to build the image using the Dockerfile and the docker buildx build
command.
docker buildx build \
"linux/amd64" \
--platform \
--build-arg QUARTO_VERSION=1.3.340 "mywebsite:1.0.0" \
--tag \
--push .
- 1
- The platform to build the image for.
- 2
- The Quarto version to use when building the image.
- 3
-
The tag to use to label the image, i.e., the name
mywebsite
and the version1.0.0
. This is useful to be able to identify the image later. - 4
-
(Optional) Assuming you have access to a registry (
docker login
), push the image to the registry, such as Docker Hub.
2.5 Deploy a container
Finally, deploying locally the image as a Docker container using docker container run
and access the website from your browser at http://localhost:8080.
docker container run \
--detach \
--platform "linux/amd64" \
--name mywebsite \
--publish 8080:80 \
mywebsite:1.0.0
- 1
- The container in detached mode, i.e., the container runs in the background.
- 2
- The platform to run the container on, i.e., same as the platform used to build the image in this case.
- 3
-
The name of the container for easy identification, i.e.,
mywebsite
. - 4
-
The port mapping, i.e., we publish the port
8080
of the host to the port80
of the container. This is useful to be able to access the website from the host. - 5
-
The image to use to run the container, i.e.,
mywebsite:1.0.0
.
Footnotes
See
renv
documentation for more information on snapshotting dependencies.↩︎See
pak
documentation for more information on snapshotting dependencies.↩︎