Skip to main content

Docker Deployment

Creating the Dockerfile

credit

We are grateful to Cindy Le and @BillChirico of Volvox LLC for sharing their experience dockerizing Docusaurus sites.

You should start by creating a dockerfile at the root of your Docusaurus project. This file contains the instructions used to build your Docker image. A Docker image is a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries, and settings.

There are a few approaches to dockerizing Docusaurus sites:

  1. Build the site in a container with the output sent to a docker volume and then use another container running a web server (like Caddy or nginx) to serve the resulting static site.
  2. Build the site in a container and run the serve process in the same container.
  3. Run the start process in a container with the local source code folder mounted as a volume.

We are going to provide a dockerfile which will cover all three of the above approaches. You can choose which approach you want to use by passing the --target option to the docker build command. Our dockerfile makes use of multi-stage builds to build the site.

# syntax=docker/dockerfile:1

# Stage 1: Base image.
## Start with a base image containing NodeJS so we can build Docusaurus.
FROM node:lts as base
## Disable colour output from yarn to make logs easier to read.
ENV FORCE_COLOR=0
## Enable corepack.
RUN corepack enable
## Set the working directory to `/opt/docusaurus`.
WORKDIR /opt/docusaurus

# Stage 2a: Development mode.
FROM base as dev
## Set the working directory to `/opt/docusaurus`.
WORKDIR /opt/docusaurus
## Expose the port that Docusaurus will run on.
EXPOSE 3000
## Run the development server.
CMD [ -d "node_modules" ] && npm run start --host 0.0.0.0 --poll 1000 || npm run install && npm run start --host 0.0.0.0 --poll 1000

# Stage 2b: Production build mode.
FROM base as prod
## Set the working directory to `/opt/docusaurus`.
WORKDIR /opt/docusaurus
## Copy over the source code.
COPY . /opt/docusaurus/
## Install dependencies with `--immutable` to ensure reproducibility.
RUN npm ci
## Build the static site.
RUN npm run build

# Stage 3a: Serve with `docusaurus serve`.
FROM prod as serve
## Expose the port that Docusaurus will run on.
EXPOSE 3000
## Run the production server.
CMD ["npm", "run", "serve", "--", "--host", "0.0.0.0", "--no-open"]

# Stage 3b: Serve with Caddy.
FROM caddy:2-alpine as caddy
## Copy the Caddyfile.
COPY --from=prod /opt/docusaurus/Caddyfile /etc/caddy/Caddyfile
## Copy the Docusaurus build output.
COPY --from=prod /opt/docusaurus/build /var/docusaurus

The dockerfile is broken up into 2 or 3 stages depending on the target. The stages are:

  • Stage 1: Base - This stage is used by all targets. It pulls a base image, enables corepack and sets the working directory.
  • Stage 2a: Dev - This stage is used by the dev target. It installs the dependencies and starts the start process.
  • Stage 2b: Build - This stage is used by the serve and caddy targets. It installs the dependencies and builds the site.
  • Stage 3a: Serve - This stage is used by the serve target. It copies the site from the build stage and starts the serve process.
  • Stage 3b: Caddy - This stage is used by the caddy target. It copies the site from the build stage and starts a caddy webserver with, optional, automatic TLS.
Automatic TLS

Caddy will automatically serve over HTTPS. It will generate a self-signed certificate if it can't use a certificate from Let's Encrypt. If you want to use a certificate from Let's Encrypt you will need to set the DOCUSAURUS_EMAIL and DOCUSAURUS_DOMAIN environment variables. You will also need to make sure that the domain name you are using is pointed to the server that you are running the container on.

You'll also need to create a caddyfile in the root of your Docusaurus project. The contents should be something like this:

{$DOCUSAURUS_DOMAIN:localhost} {
root * /var/docusaurus
encode gzip
try_files {path} /index.html
file_server
email: {$DOCUSAURUS_EMAIL}
}

This file will be copied into the container and used by Caddy to serve the site. {$DOCUSAURUS_DOMAIN} is a placeholder for the domain name that you will be using to serve the site. You can replace this with the actual domain name or you can use an environment variable to set the domain name. If you use an environment variable you will need to ensure the value is set in the container - either by passing it into the docker run command or setting it in a docker-compose file. The same applies to the {$DOCUSAURUS_EMAIL} placeholder.

Exposing application outside container

In order to make the application accessible from outside the container (e.g. from your web browser), you’ll want to set the --host option to 0.0.0.0. This can be done from your package.json:

 "scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start --host 0.0.0.0",

Or in your Dockerfile as an argument to your project’s start command:

CMD ["npm", "start", "--", "--host", "0.0.0.0"]

In order to enable live-reloading in a Docker environment, we can use --poll option (See options for more details):

 "scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start --poll 1000",

Or in your Dockerfile as an argument to your project’s start command:

CMD ["npm", "start", "--", "--poll", "1000"]

Building the Docker Image

To build the docker image you will need to run the following command:

docker build --target <target> -t <tag> .

To deconstruct the above command:

  • docker build - This is the command to build a docker image.
  • --target <target> - This is the target to build. The target is the name of the stage in the dockerfile. Valid targets are dev, serve and caddy.
  • -t <tag> - This is the name and tag of the image that will be built. The format is <name>:<tag>. The name can be anything you want. The tag is optional. If you do not specify a tag, latest will be used.
  • . - This is the path to the build context. In this case we are using the current directory as the build context.

Running the Docker Image

Depending on stage / target you will need to run the docker image differently.

To run the dev target you will need to run the following command:

docker run --rm -d -p 3000:3000 -v $(pwd):/var/docusaurus <tag>

If using PowerShell you will need to use ${pwd} instead of $(pwd). On some systems you may need to replace $(pwd) with . or the full path to the directory you want to mount.

To deconstruct the above command:

  • docker run - This is the command to run a docker image.
  • --rm - This is an optional flag that will remove the container when it exits.
  • -d - This is an optional flag that will run the container in detached mode.
  • -p 3000:3000 - This is an optional flag that will map port 3000 on the host to port 3000 in the container.
  • -v $(pwd):/var/docusaurus - This is an optional flag that will mount the current directory as a volume in the container.
  • <tag> - This is the name and tag of the image that will be run. Make sure to use the same tag that you used when building the image.
Node Modules

If you are using the dev target you will need to make sure that you have not installed the dependencies locally. The container will handle installing the dependencies for you. You will notice a node_modules folder and, potentially, other files and folders being created in your local directory as the container runs.

Docker Compose

Docker Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application's services. Then, with a single command, you create and start all the services from your configuration. We don't have multiple containers but we can still use Compose to make running the container easier.

To use Compose you will need to create a docker-compose.yml file in the root of your Docusaurus project, this will vary depending on the target you want to use and you might want to use more than one target, so you might end up with multiple docker-compose.yml files. We will provide a docker-compose.yml file for each target.

dev.docker-compose.yml
name: "docusaurus"
services:
dev:
build:
context: .
target: dev
ports:
- "3000:3000"
volumes:
- .:/opt/docusaurus
environment:
- NODE_ENV=development

To run the container using Compose you will need to run the following command:

docker compose --file <composefile> up -d --build

To deconstruct the above command:

  • docker compose - This is the command to run a docker-compose file.
  • --file <composefile> - This is the path to the composefile. Using our example composefiles, the path would be ./dev.docker-compose.yml for the dev target, ./serve.docker-compose.yml for the serve target and ./caddy.docker-compose.yml for the caddy target. Assuming the compose files are in the root of your Docusaurus project.
  • up - This is the command to bring up the containers.
  • -d - This is an optional flag that will run the container in detached mode.
  • --build - This is an optional flag that will force the container to be rebuilt.

Conclusion

You should now have a working Dockerfile and docker-compose file for your Docusaurus site. You can use these files to build and run your site in a container. You can also use these files to deploy your site to a server. You can use the docker-compose.yml file to deploy the site to a server that has Docker and Docker Compose installed. You can also use the dockerfile to build the image and then push the image to a registry like Docker Hub or GitHub Container Registry and then pull the image onto the server and run it.