How to Containerize Your Docusaurus (And Why You Should)
Tags: docker, tutorial, documentation, beginners
Published: 2024-01-25 19:24:31

*[This post was originally published on the Shipyard Blog](https://shipyard.build/blog/containerize-your-docs/)*

---

Containerizing your project’s documentation might be one of the easiest ways you can remove friction from iterating. Your docs are also probably one of the easiest things you can containerize. Here’s how you can get your docs up and running with Docker Compose in just a few minutes.

## Why should I containerize my docs?
Simply put, containerizing your docs lets you hit the ground running when writing documentation.

Here are a few QoL improvements that come with doing this:

- No need to install/update/manage dependencies
- Work with the same development environment across machines
- Start developing with just a single command

In short, it’s just easier. You don’t have to think about building and running the dev server, you can just open your terminal and your IDE, then start writing. When writing docs, you’re likely iterating often and adding small chunks at a time, so it makes sense to optimize your local dev environment to spin up as efficiently as possible.

## Containerizing your Docusaurus build

At [Shipyard](https://shipyard.build), we’re big fans of <a href="https://docusaurus.io" target="_blank">Docusaurus</a>. We’ve been using it to write our <a href="https://docs.shipyard.build/docs" target="_blank">public documentation</a> for three years. We’ve also been using <a href="https://docs.docker.com/compose/" target="_blank">Docker Compose</a> for local docs development just as long. A lesson we've learned is that sometimes the best tools are the ones we're already using. We were using Docker Compose for everything else, why not extend it to our docs?

### Writing the Dockerfile

Since Docusaurus is <a href="https://nodejs.org/en" target="_blank">Node</a>-based, the Dockerfile is pretty standard. You might need to swap in your docs’ custom run commands.

```Dockerfile
FROM node:16-alpine

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

CMD ["npm", "start"]
```

#### This Dockerfile Explained
If you’re newer to <a href="https://docs.docker.com" target="_blank">Docker</a>, here’s a line-by-line rundown of what this Dockerfile is doing:

1. Start with a Node base image
1. Create and set the working directory within the container
1. Copy the Node dependencies to the working directory
1. Install dependencies within container
1. Copy the rest of the repo’s contents to the working directory
1. Run the Node project’s start command

### Setting the host

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`:

```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:

```Dockerfile
CMD ["npm", "start", “--host”, “0.0.0.0”]
```

### Writing the Docker Compose file

Now that we have the Dockerfile, we can write a single-service Compose file to leverage it. Here, we’ll specify any Docker config options (so we don’t need to remember them for complex `docker build` and `docker run` commands).

```YAML
version: '3'

services:
  docusaurus:
    build:
      context: .
    ports:
      - '3000:3000'
    volumes:
      - './docs:/app/docs'
      - './src:/app/src'
      - './static:/app/static'
      - './docusaurus.config.js:/app/docusaurus.config.js'
      - './sidebars.js:/app/sidebars.js'
```

Because we’re optimizing this Compose file for local development, we’ll want to define bind mounts from any files and directories that we plan to edit. This way, any local changes will be reflected in the Docker container.

In this example, I have a few bind mounts defined under the volumes option. The syntax for this involves specifying the local path (relative to the build context), followed by a colon, then followed by the corresponding location in the container.

### Bonus: Writing the .dockerignore

Docker images tend to run pretty large, so writing a `.dockerignore` can help slim them down by weeding out any files irrelevant to your Docker build.

To exclude any Markdown files in the root directory (e.g. your `README.md` and `LICENSE.md`, but not your *actual* Markdown doc pages), you can use this glob pattern in the `.dockerignore`:
```r
/*.md
```
Your Docker build also doesn’t need to include the Dockerfile or the Compose file. These glob patterns will match most naming conventions for those:
```r
*compose*
*Dockerfile*
```
You can also exclude `node_modules` (likely the biggest bloat culprit):
```r
node_modules
```

### Docs that work right out of the box
Now that you’ve containerized your Docusaurus docs, you can spin them up locally with `docker compose up` and start editing.

Thanks for reading! 

*If you enjoyed this Docker Compose tutorial, [check out this post I made a few months ago on how to write a Compose file for a Wordle clone](https://shipyard.build/blog/react-wordle-with-docker-compose/).*