Get up and running with docs-as-code!
DocOps Box is a documentation multi-tool for your Mac or PC.
This project includes an introductory guide and quick-start procedure for establishing and maintaining a broadly capable environment for documentation management and document-processing work.
DocOps Box uses Docker and VS Code Dev Containers to provide a consistent, reproducible environment for digital document management across different operating systems and team members.
The provided system also works for solo practitioners who want a stable, isolated environment for their docs projects without the hassle of managing dependencies on their host system.
|
💡
|
LLMs or people engaging LLMs/agents to help them use DocOps Box should reference the Agent User Guidance skill. |
Even if you don’t use the images or tools provided, this guide should still be helpful for anyone orienting to the world of docs-as-code tools and workflows. It includes a section for “host installation” of all the tools supported by the container approach, so you can choose your own adventure if you prefer to set up your workstation without Docker.
This project is geared as a guide and assets for meeting non-developer tech or clerical workers, typically on their Windows or maybe MacOS systems, not yet set up as “development environments” with tools like Ruby, Node.js, Python, Git, Pandoc, Vale and the world of capabilities that these tools open up.
|
📎
|
This entire project is geared toward Windows, Mac, and Linux users, but it assumes virtually zero Linux/Unix knowledge or preference. You will be using a command line (terminal), and it needs to be (1) Mac-based or (2) Linux-based (including via WSL2 on Windows). |
This project is intended to meet users where I have found these “tech-savvy non-programmers” among my clients the past several years, such as technical writers, project managers, and professional document wranglers like paralegals, non-software engineers, researchers, and educators.
This toolkit is intended to provide a proper swath of technologies in the form of a specially designed Docker image🔖 you can run as a container🔖 on your system, whatever that system may be.
DocOps Box is specifically geared toward the AYL DocStack: applications using AsciiDoc, YAML, and Liquid to build documents and documentation.
However, the max images can handle a huge swath of the most common tools and formats used in the docs-as-code world, such as JavaScript, Markdown, reStructuredText, JSON, XML, and more.
|
📎
|
DocOps Box is tested on Docusaurus, Antora, Astro, Jekyll, MkDocs, Sphinx, and 11ty.
The max images are suitable for projects running any of these platforms as well as all the pre-installed tools found in Tooling Overview.
|
The main intent is to provide the basics for these fairly technical/non-expert users to operate with the power of advanced “docs-as-code” techniques that only programmers, hackers, and IT professionals have typically bothered messing with until recently. See Who is This Software For? for more on the intended user base and use cases for this project.
If you know you want to get started with DocOps Box, skip to Prerequisites and perform any necessary installations there.
The next few sections are for users who want to understand the rationale and context for this project before diving in.
The only real prerequisite for the “Dockerized” (“containerized”) approach to establishing a robust DocOps environment, as provided in this project, is that you have Docker working on top of a Unix-like shell🔖.
It makes the most sense for users who need a consistent mix of tools across multiple projects; setting up a separate Docker image per tool is at least as complicated as a native install.
|
📎
|
Windows users will need to set up a WSL2 kernel and a Linux distribution if they have not already done so. This much is true of any Windows-based approach advised by this project. |
Technologies included in the DocOps Box Docker images are:
- Zsh
-
The powerful and elegant terminal shell environment that is more user-friendly than Bash, already configured with OhMyZsh.
workimages only;liveimages use Bash - Git
-
The most popular version control system, which is used to track changes to your codebase and to share it with others
- Ruby
-
A runtime environment🔖 for executing Ruby command-line utilities and managing Ruby dependencies
- Node.js
-
A runtime environment for numerous auxiliary tools that have no Ruby equivalent; included in
maximages only - Python
-
A runtime environment for the inevitable Python utilities; included in
maximages only - Pandoc
-
An extraordinary document migration utility that can convert between a huge range of formats
- Vale
-
A document validation utility (linter) that can check markup-formatted writing for consistent style and preferred grammar
- OpenAPI tools
-
Three excellent OpenAPI Specification🔖 utilities, for validating/linting OAS documents and generating API reference documentation from them (Redocly CLI, Vacuum, and Speakeasy CLI)
- text editors
-
The images provide the GNU nano (default) and Vim, TUI🔖 utilities for quick edits in the interactive shell.
- LibreOffice
-
Powerful CLI utilities behind the popular office suite; included automatically in
maximages - Ruby tools
-
Out of the box (so to speak): Asciidoctor (
.adoc→.html/.pdf/.epub), Kramdown-AsciiDoc (.md→.adoc), Nokogiri (HTML/XML parsing), and other handy Ruby gems. Add your own per project usingGemfile; see Adding Runtime Dependencies.
See the project’s Dockerfile for the full list of installed packages and utilities.
Many are secondarily documented below in Auxiliary Tools as well as Installing Everything to Host.
This environment is suitable for users to execute scripts and runtime applications like the ones I provide for and use with my clients. It’s what you need to apply my or anyone else’s Ruby-based documentation toolchain or tech stack to your specific purposes.
This project should also suit if your toolchain is Javascript-based (Docusaurus, Antora, Next.js, SvelteKit, 11ty, Astro, etc) or Python-based (Sphinx/ReadTheDocs, MkDocs). However, if your toolchain excludes Ruby altogether, this Ruby-centric project might not be the most efficient route to a stable coding environment.
This project is not intended to be a “one size fits all” solution for every docs-as-code project; it is optimized for relatively complex situations (multiple projects, multiple/complex toolchains, etc).
Use this table to decide if DocOps Box is right for your situation.
| Scenario | Recommendation |
|---|---|
You occasionally use one or two DocOps Lab tools |
Use each tool’s own Docker image directly |
You regularly use multiple DocOps Lab tools |
Use DocOps Box ( |
Your toolchain uses Ruby and Node/Python tools |
Use a DocOps Box |
Your toolchain uses runtimes other than Ruby, Node, or Python |
DocOps Box will be partially helpful or unsuitable; advanced users might consider extending the image |
You are setting up a team documentation environment |
Use DocOps Box with a shared |
You need a CI/CD🔖 pipeline for docs automation |
Use DocOps Box |
DocOps Box, as a project, is not about pushing you into using any specific software, including our Docker images or our dxbx script.
Full “native” or host installation instructions for all core DocOps Box-supported components are included in an appendix: Installing Everything to Host.
| Criterion | Host Install | DocOps Box |
|---|---|---|
Setup time (first use) |
45-90 min |
~5 min |
Reproducibility across machines |
High effort |
Automatic via config files |
Team-wide standardization |
Requires active coordination, docs |
Git-committed |
Works on Windows |
WSL2 + manual setup |
WSL2 + Docker |
IDE integration (VS Code) |
Full native |
Full via Dev Containers extension |
Per-project dependencies isolation |
As configured per runtime/project |
Named volumes 🔖 handled by |
Suitable for CI/CD |
Requires environment setup steps |
Available via |
All of the software required to run DocOps Box is free, open source, and strongly recommended for any modern code-like documentation workflow. You cannot go wrong having VS Code and Docker on your workstation, even if you do not end up using DocOps Box over the long term.
|
💡
|
This document is intended to guide you to competency in the world of docs-as-code, not merely using dxbx and its Docker containers.
Everything advised has a graceful fallback to direct installation on your host system.
|
Work through the following steps in order.
- Prerequisites:
This section is somewhat long but should move along quickly. All of these apps are critical, some are likely already available on your system, and this guide covers installing them on any major platform.
There is no avoiding the command line in the docs-as-code world. If you already have a terminal you like, use it and skip ahead.
|
💡
|
It is strongly advised that you choose a terminal app other than the one in VS Code, simply for differentiation of duties, at least when getting started.
Use VS Code’s terminal inside the containerized environment; use an external terminal for dxbx commands and other host-level work.
|
If you are unaware of or unhappy with your terminal app, here are my recommendations per operating system.
By far my two favorite terminal apps are Warp and Wave.
Both are freemium open-source models with generous free tiers.
Warp has a fully integrated terminal that can be a little bit overwhelming.
Wave is a chat-only tool for now, so you have to copy and paste commands and output back and forth, though the chat can automatically read local files.
-
Download Warp (downloads for all platforms in page footer)
If you prefer to install from an app store or package manager, here are some good options on all three platforms.
-
Windows users can use the built-in Windows Terminal, which supports multiple shells including PowerShell and WSL2. If it’s not installed, get it from the Microsoft Store: Windows Terminal.
-
MacOS users can use the built-in Terminal app, but most users prefer iTerm2, which is also available as a Homebrew package:
brew install --cask iterm2
-
Linux users are unlikely to find better options than Wave or Warp. If you are a Linux user who does not already have a preference, try one of those or your distro’s default terminal app.
The recommended daily workflow uses VS Code with the Dev Containers extension as your main interface.
|
📎
|
VS Code is not truly a requirement for the DocOps Box workflow, but it is strongly recommended for novice users without a strong reason to choose another workflow. Your favorite IDE/text editor is just as valid, in combination with its own terminal or another that you prefer. If you have no strong preference, give VS Code a try. |
-
Install Visual Studio Code.
📎Linux/GNOME users:On first launch, GNOME may prompt “Choose password for new keyring”. Press Escape to dismiss it; no password is needed for local development. -
Install the Dev Containers extension from the VS Code Marketplace:
ms-vscode-remote.remote-containers
(Or search for “Dev Containers” in the Extensions panel.)
Whenever your workspace path in VS Code is configured with a .devcontainer/devcontainer.json, VS Code will prompt you to “Reopen in Container”, which gives you the full DocOps Box environment with all the tools installed and configured.
Or, you can always use dxbx vsc from the project root directory in your terminal to open a VS Code window with the containerized environment.
- Recommended plugins:
-
-
(Windows/WSL users) Remote Development Extension Pack from Microsoft (includes Dev Containers)
-
(MacOS/Linux users) Dev Containers from Microsoft
-
- Recommended for AsciiDoc/YAML/Liquid work:
- Recommended configuration changes under File › Preferences › Settings:
-
-
Set Wrapping Indent to
indent -
Set Editor: Format On Save to
true
-
- Privacy/security settings:
-
-
Search
telemetryand disable where you prefer:-
Set Telemetry: Feedback to
disabled -
Set Telemetry: Telemetry Level to
off
-
-
Set Extensions: Auto Update to
true
-
|
💡
|
|
The quick-start procedure will ensure you have Bash 4 or higher on your host. For legal reasons, MacOS comes with Bash 3, but it is highly advised and 100% harmless to add Bash 4+ to your system.
|
📎
|
Adding Bash 4 does not replace Z Shell as your default system/interactive shell. |
The bootstrap script will offer to install Bash 4+ via Homebrew (as well as Homebrew itself) if not already present.
Like all prerequisites, Bash 4 is an essential tool for working with developer tools and workflows.
|
💡
|
Mac users can skip to Docker (All OSes). |
|
💡
|
If you are on MacOS or Linux, skip this step. |
The most reliable way to use a Unix-like shell on Windows is to install via the oddly named Windows Subsystem for Linux or WSL2. This is the biggest step for Windows users, but it is also straightforward and well-documented by Microsoft.
-
Ensure your Windows version supports WSL2.
Press Win+R, type
winver, and press Enter. If your version is Windows 10 Build 19041 or higher, or any Windows 11, you can proceed. If not, use the manual process. -
Open Windows Terminal as an administrator.
Right-click the Windows Terminal icon and select “Run as administrator”.
-
Run the WSL2 installer:
Standard Ubuntu WSL installationwsl --install
This installs WSL2 and Ubuntu in one step.
💡While Ubuntu is by far the most documented distribution commonly used with WSL, DocOps Lab recommends Fedora, which is a less bloated and less commercial distro.
wsl --install -d FedoraLinux-43
That last command should perform the entire setup procedure.
|
📎
|
If this procedure does not work, follow the official Microsoft installation guide. |
To enter a WSL2 session in the future, open your terminal client and enter wsl.
|
❗
|
All project folders intended for use with DocOps Box should live inside the WSL2 filesystem (~/…), not on the Windows mount (/mnt/c/…).
File I/O through the Windows mount is significantly slower and causes subtle permission issues.
|
Curl is a command-line utility for making HTTP requests, which is used to download the dxbx script.
It is a common and highly recommended tool, but it may not come preinstalled in all Linux distributions, including Ubuntu via WSL2 on Windows.
curlcurl --version
|
💡
|
If curl is available, proceed to Docker (All OSes).
|
If curl not present, install it with your package manager.
curl for Debian/Ubuntusudo apt-get update sudo apt-get install curl
Docker is a platform that allows you to run a containerized Linux environment on your system. Containers are much lighter and more customizable than full virtual machines. They are specifically designed for creating consistent environments for development and automation.
See Threading the needle with Docker, Architecture Decision Rationale, and most usefully the Docker glossary section for more context. This section covers the nuts and bolts of installation.
Microsoft maintains documentation for setting up Docker with WSL2.
|
💡
|
Follow that guide, then move on to DocOps in a Box: Quick-start Guide. |
The preferred method is the Docker Desktop installer.
Alternatively, use Homebrew. Follow these instructions to install Homebrew if needed as well as full instructions for installing and starting Docker.
|
|
Linux users running under WSL2 should install Docker according to Docker on Windows (WSL2). Do not install Docker directly onto the WSL2 host instance. |
Install Docker Engine using Docker’s official convenience script:
curl -fsSL https://get.docker.com | sudo sh
The script prints progress and some informational notes about alternative configurations, which you can ignore. When it finishes, enable Docker to start automatically at boot and start it now:
sudo systemctl enable --now docker
Then grant your user permission to run Docker without sudo:
sudo usermod -aG docker $USER
|
📎
|
For distro-specific or manual installation, see Docker’s official install docs. |
docker --version && docker compose version
Both commands should print a version number with no errors before you continue.
If you have all the prerequisites in place, the recommended quick-start procedure is to use dxbx and a DocOps Box work image to get a containerized environment up and running in just a few minutes.
In any directory you maintain docs in…
curl -fsSL https://raw.githubusercontent.com/DocOps/box/refs/heads/latest scripts/dxbx-bootstrap.sh -o dxbx-bootstrap.sh chmod +x dxbx-bootstrap.sh sh ./dxbx-bootstrap.sh dxbx init rm dxbx-bootstrap.sh dxbx ex docops info
For a more detailed, explanatory walkthrough of the same procedure, see step through the next sections.
If this quickest start set up worked for you, skip to Step 4: Start working.
If you have not installed dxbx yet, copy, paste, and enter this once in your host shell:
curl -fsSL https://raw.githubusercontent.com/DocOps/box/refs/heads/latest scripts/dxbx-bootstrap.sh -o dxbx-bootstrap.sh chmod +x dxbx-bootstrap.sh sh ./dxbx-bootstrap.sh
What this procedure does
-
Downloads the
dxbx-bootstrap.shscript to the current directory -
Makes the script executable
-
Executes the bootstrap procedure, which performs the following steps:
-
Checks for Bash 4, curl, and Docker, and prompts you to install any missing prerequisites
-
Installs
dxbxto${XDG_BIN_HOME}(defaults to~/.local/bin/dxbx) and adds it to yourPATH🔖 if not already present
-
-
Prints instructions to activate
dxbxin your current terminal session -
Cleans up by removing the downloaded
dxbx-bootstrap.shscript
|
💡
|
When any script/command prompts for [y/N] or [Y/n], it is requesting approval (y for yes; n for no).
The capital letter indicates the default selection, so you can just press Enter instead of typing the character to choose the default.
|
If ${XDG_BIN_HOME} (or ~/.local/bin if XDG_BIN_HOME is not set) is not yet in your PATH🔖, the installer will prompt you to add it automatically; answer y.
Then activate it in your current session by running the export command it prints, or simply open a new terminal window.
|
💡
|
Skip this step if your project is already configured for DocOps Box use. If you do not know, there is no harm in trying. |
|
💡
|
It is always advised to stash or commit changes using Git before installing new software in any repository. git stash push -m "Stash before initializing DocOps Box" |
In your project directory:
dxbx init
This downloads docopsbox.yml and .env into .config/, makes .devcontainer/devcontainer.json, prompts for a project slug, and updates .gitignore automatically.
ls -a .config/ .devcontainer/
To test that dxbx is working, run this command in your terminal:
dxbx ex docops info
You should see a nicely formatted readout of available tools and be returned to your host shell prompt.
What was that?]
The second half of the above command (docops info) is run inside any DocOps Box container for a readout of tool availability and versions.
The first half (dxbx ex) is the command for executing any command in a one-off container, which is created, run, and removed automatically.
When you need the tools provided by DocOps Box, invoke them inside the container, not on your host system. Typically, you will likely wish to work inside the container interactively, either in VS Code or dedicated terminal app.
Choose whichever entry point suits you; all give you the same environment, and of course you may alternate.
Try dxbx up to experience a full interactive shell session inside the container.
Use dxbx vsc to open a VS Code window with the containerized environment.
Or use dxbx ex to run any command in a fresh container and return to your host prompt immediately.
See the next section for more orientation and explanation of the different ways to work with your new environment.
As much as DocOps Box tries to abstract away the complexities of Docker, some familiarity with the underlying concepts and commands is helpful for troubleshooting and advanced use.
There are also various ways to invoke these environments in your day-to-day work.
The first thing that confuses new users is that you now have two command environments to keep track of: the host shell (your normal terminal) and the container shell (the Linux environment inside Docker, where your tools actually live and run).
Your computer operating system or shell is the host system.
You are virtualizing a purpose-built Linux operating system in a container, which exploits your host’s Linux/Unix kernel.
|
📎
|
If you are using all of this inside WSL2 on Windows, ignore the fact that your Windows OS is also a host. For our purposes, the Linux shell you are running is your host, and Windows is your OS. |
So while you may sometimes have two (or more) command prompts to consider, we will specify the host (where you run dxbx commands) or the container (where you run DocOps tools).
Instructions for any third-party Ruby or other CLI utilities (including other DocOps Lab apps) should be performed from within a DocOps Box container.
Mac and Linux users have two layers:
Terminal app → container shell (Zsh) (host)
Windows users via WSL2 have three layers; you are virtualizing twice:
Windows Terminal → WSL2 Linux shell → container shell (Zsh) (PowerShell) (host)
The important thing for Windows users: dxbx commands are typed in the WSL2 shell, not in PowerShell or a Windows Command Prompt.
WSL2 is your host for everything that follows.
| Method | How to enter | What happens to your prompt |
|---|---|---|
VS Code + Dev Containers |
Enter |
Terminal in the Dev Container window runs inside the container. |
Interactive shell |
Enter |
Your prompt changes to the container shell.
You stay there until you type |
Get in and out |
Enter |
Runs the command in a fresh container, prints the output, and removes the container. No change to your prompt. |
|
💡
|
Not sure which shell you are in?
Run hostname.
If it prints a short hex string (like a3f9d2b1), you are inside the container.
If it prints your machine name, you are on the host.
|
Some programs you can run inside the container have their own interactive shells (like irb for Ruby or node for Node.js).
Use the following table to determine what to type to detect or exit a shell.
| Scenario | Prompt | Command |
|---|---|---|
Detect PowerShell (Windows only) |
|
You are in PowerShell, not yet in Linux.
Type |
Detect WSL2 shell (Windows only) |
|
You are in WSL2 Linux.
Confirm with |
Exit WSL2 back to PowerShell (Windows only) |
|
|
Detect container vs host |
|
|
Exit container to host shell |
|
|
Exit |
|
|
Exit |
|
|
Exit |
|
|
DocOps Box uses named volumes🔖 to persist data across sessions.
Your shell-command history is preserved in a volume that is used across all projects, so your command history is conveniently available for navigation and autocomplete.
Dependency packages are maintained for convenience, so they need not be reinstalled every time you start a session.
docops-shell-history-
Mounts at
/commandhistory. Shared across all projects. Contains your Zsh history. Do not delete without backing up first. docops-<slug>-bundle-
Per-project Ruby gem cache. Mounts at
/usr/local/bundle. Safe to recreate at any time;bundle installrepopulates it. docops-<slug>-node-
Per-project Node.js packages. Mounts at
/workspace/node_modules. Safe to recreate;npm installrepopulates it. An emptynode_modules/directory will appear in your project root. This is Docker’s mount point; the actual contents are inside the named volume and invisible to the host. docops-<slug>-python-
Per-project Python virtual environment. Mounts at
/opt/venv. Safe to recreate;pip install -r requirements.txtrepopulates it. Runpip installnormally inside the container; no flags needed.
If your volumes ever become problematic or stale for any reason, it is safe to remove the disposable ones, which will be recreated on demand.
| Command | Effect |
|---|---|
|
Show container state and volume sizes. |
|
Remove the container and all per-project dependency volumes (Ruby gems, Node packages, Python venv); preserve shell history. |
|
Remove dependency volumes and shell history (requires double confirmation). |
|
Copy shell history to |
|
Restore shell history from a backup (append or replace). |
In most cases, you will likely only need a single image for starting containers on your local machine.
Thus, the main invocation commands will (dxbx up, dxbx ex) execute a container based on the default image.
In case you wish to use an alternate image/container, dxbx up, dxbx ex, and dxbx pull subcommands all accept an optional image specifier as their first argument.
This selects the image for that invocation without editing any files.
| Form | Selects |
|---|---|
|
Variant only; context from config |
|
Context only; variant from config |
|
Both variant and context |
|
Same as |
|
📎
|
It is not possible to run a live image in interactive mode.
Live images are intended for one-off (dxbx ex CMD) commands.
|
min:live image (no running container needed)dxbx ex min:live bundle exec rake test
What just happened?
The above command runs bundle exec rake test in a new container based on the bare-bones box-min:live image.
max:work imagedxbx up max:work
What just happened?
The above command starts a new container based on the box-max:work image and gives you an interactive shell inside it.
In a default environment, this is the same as just running dxbx up with no image specifier.
dxbx ex work 3.4 ruby ./scripts/test-script.rb
What just happened?
The above command runs ruby ./scripts/test-script.rb in a new container based on the box-work:3.4 image, which has Ruby 3.4 installed.
Shell-level environment variables (IMAGE_CONTEXT, IMAGE_VARIANT) also perform this function.
|
📎
|
If you alternate between Ruby versions, you will need to install dependencies for each version. The Bundler volume will remain the same, but the gems will be installed in separate subdirectories per Ruby version. |
The settings for PROJECT_SLUG and IMAGE_REGISTRY can only be overridden via environment variables.
Inside the container, do not install tools with one-off gem install, npm install -g, or pip install commands (even though it will work).
This also goes for shell programs; avoid using apt-get install or similar dependency manager🔖 commands to add tools to the image.
Instead, declare any runtime libraries in a manifest file🔖 and let the package manager install the whole set. This best practice keeps your environment reproducible: anyone on your team (or a CI/CD operation) gets identical versions from the same manifest.
| Runtime | Manifest file | Install command | Notes |
|---|---|---|---|
Ruby |
|
|
Gems persist in the |
Node.js |
|
|
Packages persist in the |
Python |
|
|
Packages persist in the |
These install commands run automatically at container creation inside VS Code. You only need to re-run them manually if you edit the manifest mid-session.
|
💡
|
If a tool you need is not available as a gem, npm package, or pip package, it may already be installed system-wide in the image (Pandoc, Vale, Git, etc.).
Check with which <tool> before reaching for a package manager.
|
If your package files are committed in Git and you don’t want to force your whole team to install a tool you need temporarily, you can add it to the image with a custom image build or Dockerfile. See Extending DocOps Box.
For the most part, the whole Docker-based approach of DocOps Box is intended to stick to a per-codebase strategy.
While it is nice to have configuration-free tools like Pandoc and Asciidoctor available anywhere in your host shell, even Docker and dxbx cannot abstract away the complexity of persistence throughout a host system.
If you do need more tools directly installed in a Docker image, consider Extending DocOps Box.
If your project includes a server component (like Jekyll’s bundle exec jekyll serve), you can expose the port to your host machine using properties in the configuration files.
.config/docopsbox.ymlservices:
docops:
ports:
- "4005:4005"For VS Code Dev Containers, the port also needs to be forwarded in the Dev Container configuration.
.devcontainer/devcontainer.json"forwardPorts": [4005]Be sure the serve command inside the container is configured to bind at 0.0.0.0, and the port is specified to match the forwarded port.
bundle exec jekyll serve --host 0.0.0 --port 4005
Once the server is running, you can access it from your host machine at http://localhost:4005 (or whatever port you forwarded).
DocOps Box configuration involves 4 main files:
-
.config/.env(committed to Git; non-secret configuration) -
.config/.env.local(not committed; secret configuration) -
docopsbox.yml(committed; Docker Compose configuration) -
.devcontainer/devcontainer.json(committed; VS Code Dev Container configuration)
By design, you may never need to touch these just to maintain a working environment.
The dxbx init command sets them up for you, if you are starting a new project or adding DocOps Box to an existing project.
If you are joining a project, hopefully these files have been established and shared with you.
In cases where configuration tweaks are needed, most can be done in the .env or .env.local files, which are straightforward.
The .config/.env file is committed to your repository.
It contains only safe, non-secret project-specific configuration.
| Variable | Default | Description |
|---|---|---|
|
see below |
Short identifier used to name the per-project gem volume. Set this to something unique across your projects. |
|
|
|
|
|
|
|
(unset) |
Selects a specific Ruby version. Omit (or leave unset) to use the default (3.3). |
|
|
Docker Hub username or registry prefix for the image. |
The PROJECT_SLUG environment variable is used to create unique volume names for each project, so that dependencies installed in one project do not interfere with those in another.
It defaults to the parent directory name, but you can set it to any short identifier you like.
|
📎
|
|
The .config/.env.local file is created during dxbx init.
Uncomment values as needed. This file is never committed to Git.
| Variable | Use |
|---|---|
|
Override the UID🔖 used for file ownership inside the container.
Set to match |
|
Same as above for group ID ( |
|
Override to a private registry or local mirror. |
The dxbx utility resolves the PROJECT_SLUG in this order:
-
PROJECT_SLUGfrom.config/.envor.config/.env.local, if present, or else -
:this_proj_slug:AsciiDoc attribute inREADME.adoc, if present, or else -
current directory name (spaces and underscores to hyphens, all lowercased)
The image tag format is: <registry>/box-<variant>:<context> or, for a specific Ruby version, <registry>/box-<variant>:<context>-<ruby>.
For example: docopslab/box-max:work or docopslab/box-max:work-3.4
| Setting | Values | Notes |
|---|---|---|
|
|
|
|
|
|
|
|
Selects a specific published Ruby version; omit to use the default (3.3).
Setting this appends the version to the context tag: |
The .config/docopsbox.yml file is the single source of truth for the container structure.
Both dxbx and .devcontainer/devcontainer.json reference it, and docopsbox.yml in turn references .config/.env and .config/.env.local as needed.
-
env_file:loads.envfirst, then.env.local(required: false; no error if absent; both resolved relative to.config/) -
Service name:
docops; container name:docopsbox_${PROJECT_SLUG} -
Volume names embed
PROJECT_SLUGfor per-project isolation -
Working directory inside the container:
/workspace
The .devcontainer/devcontainer.json file configures how VS Code connects to the container.
Hopefully, you should never need to edit this file, as it references docopsbox.yml for all relevant configuration.
Use the Development Containers standard documentation for any necessary tweaks or troubleshooting.
Run this first when something is not working.
dxbx stat
This covers the most common problems:
-
Docker Engine version and availability
-
Docker Compose version
-
Image presence and tag verification
-
Volume existence and size inspection
-
WSL2 environment detection
-
Actionable error messages for common failure conditions
- 1:
Error: No such service: docops -
dxbxis not running from the directory containing.config/docopsbox.yml.-
Change to the project root and try again, or
-
Use
dxbx initto set up the configuration files.
-
- 2: Permission denied writing files on Linux
-
Files the container writes are owned by the container user, which may differ from your host user. Set
HOST_UIDandHOST_GIDin.config/.env.localto match your host user:# Find your host UID and GID id -u # prints your UID id -g # prints your GID
config/.env.localHOST_UID=1001 # replace with output of: id -u HOST_GID=1001 # replace with output of: id -g
Then re-run
dxbx up. - 3:
bundle installfails inside the container -
-
Be sure
Gemfileis in the mounted project root.ls -l /workspace/Gemfile
-
Be sure your present working directory is
/workspaceinside the container.pwd
-
The bundle volume may have stale or corrupted gem data. Clear and rebuild it:
dxbx wipe --vols dxbx up # Inside the container: bundle install
-
- 4: Gems fail to load after updating Ruby version
-
Native gem extensions (compiled C code inside gems like
nokogiriorffi) are compiled for a specific Ruby version. If the image’s Ruby version changes and you are using a cached gem bundle volume, the extensions will be incompatible.Clear the bundle volume and reinstall:
dxbx wipe --vols dxbx up # Inside the container: bundle install
If you are not sure which Ruby version the cached bundle was built against,
dxbx statshows the current image’s Ruby version. - 5: VS Code Dev Container fails to start
-
-
Verify Docker is running:
docker info -
Validate the Compose file:
docker compose config -
Open the Dev Container log: Command Palette → Dev Containers: Show Container Log
-
- 6: WSL2: very slow file I/O
-
Ensure your project directory lives inside the WSL2 filesystem (
~/…), not on the Windows mount (/mnt/c/…). Windows filesystem mounts have significantly degraded I/O performance under WSL2.
This advanced topic is only cursorily covered here, but DocOps Box is designed to be extended and customized in various ways.
There are several currently undocumented features, including the use of pre- and post-build scripts and serial image builds with custom Dockerfiles.
This section will grow as real-world conditions prove out use cases.
The Dockerfile used to build supported images is highly configurable.
It accepts lots of arguments for a custom build.
You will want to clone the DocOps/box repository and run docker build inside it.
-
Clone the repo (outside your project directories).
-
With SSH:
git clone git@github.com:DocOps/box.git docops-box
-
Without SSH:
git clone https://github.com/DocOps/box.git docops-box
-
Enter the new directory.
cd docops-box
-
Build a custom image with your preferred build arguments.
Example build command with some common argumentsdocker build --network host \ --build-arg IMAGE_CONTEXT=work \ --build-arg RUBY_VERSION=3.4 \ --build-arg ADD_NODEJS=true \ -t docops-box/custom:work \ .
-
The -t argument tags the image.
The . as the last argument indicates that the Dockerfile is in the current directory.
See the ARG directives in the base Dockerfile for all available build arguments.
The dxbx utility, executed in the DocOps Box repository codebase, can build several custom permutations locally.
The format is:
[EXTRA_ARGS ].bin/dxbx make [variant] [context] [ruby_version]
- Example image build commands
-
Builds
docopslab/box-min:workwith Ruby 4.0./bin/dxbx make 4.0 min work
Buildsdocopslab/box-max:livewith Ruby 4.0 and Node.jsADD_NODEJS=true NODEJS_VERSION=26 ./bin/dxbx make min live 4.0
If you do build a custom image with additional software pre-installed, you can run it from anywhere using a complete docker run command.
For instance:
docker run --rm -it -v $PWD:/workspace docopslab/custom:work asciidoctor -o readme.html -a doctype=book README.adoc
If you want to use dxbx commands and/or wish to share it with your team, do the following.
-
Keep the image tag consistent with its closest DocOps Box variant, but use your organization’s own registry prefix.
docker build -t ourco/box-min:work .
-
Host the image on DockerHub.
-
Change the
registryproperty in your.envto point to your account instead ofdocopslab.
This should execute dxbx commands on images sourced and built as you choose.
I made the DocOps Box toolkit for people who want to learn my preferred document operations automation tools but who do not yet want to deal with properly installing and maintaining Ruby, Node.js, Python, Pandoc, Vale, Git, and more on their system.
If you are less technical than programmers but bolder and more experienced than most peers in your profession, this entire project is geared toward getting you up and running with tools to bridge the gap.
This solution also provides an environment to share amongst your team, or even to use on a production server or continuous-integration/deployment process, without everyone having to install all the dependencies one by one.
If you are working solo, it’s even simpler to get started.
Install the prerequisites, install dxbx, and run dxbx init in your project directory.
The default setting should work for you, and you can start running commands right away.
|
💡
|
Once you are working regularly with these technologies, it may well make sense to set up a proper development environment locally, especially if you find yourself directly invoking command-line tools frequently for similar or parallel projects. This guide provides instructions for doing just this on all three major operating systems. |
The repo includes a command-line application (dxbx), mainly for simplifying Docker commands.
Docker is an incredibly powerful and somewhat complicated piece of software, but we use it in specific ways that do not require mastery or even a full grasp of what Docker is and does.
More importantly, the dxbx script simplifies the commands you will need to run for this specific Docker use case, without pretending to be a broad controller for other Docker use cases.
This “abstraction” manages Docker images and containers such that the running container will:
-
reflect your host workstation user and group, Git config, and SSH keys
-
work seamlessly with your local Git environment if you already have one
-
write to your own (host/workstation) filesystem so your work remains available when the container shuts down
-
persist the state of your project, including dependencies and command history, across container restarts
Terms of art used throughout this document and the broader “DocOps”/docs-as-code domain.
- docs-as-code
-
A set of practices for managing technical documentation with the same tools and workflows as software development. This includes using version control (Git), writing in lightweight markup formats (like AsciiDoc or Markdown), and automating builds and deployments with CI/CD pipelines. The goal is to treat documentation as a first-class citizen in the development process, improving collaboration, versioning, and quality.
- DocOps
-
Short for document operations, a set of practices and tools for managing technical documentation with the same (or analogous) techniques and tools as software development.
DocOps tends to be focused on automation and tooling; it pertains to “practitioner services” aspect of the docs-as-code practice. DocOps Box is so-called as it aims to assist document operators by providing the underlying infrastructure for executing a docs-as-code workflow.
- bootstrap
-
A process for rapidly initializing or instantiating a project/codebase from minimal inputs. The process typically involves “inflating” files from templates and applying enough configuration to enable basic/nascent operations.
In the world of CLI tools and shell environments, “bootstrapping” often means performing an
initcommand that writes files and prepares for or executes a first invocation of whatever program is being set up.
- technical documentation
-
Documents that are structured, versioned, highly semantic, single sourced, divergently delivered, or otherwise complex to the extent of requiring special handling for the management, maintenance, or automated/repeat publishing. This includes legal documents, standards specifications, product requirements, reference materials, non-linear narratives, curriculum, as well as any “living” document, collaboratively authored material, or anything intended to diverge from but maintain association with a prime or peer document.
- tech stack
-
The core, categorical or platform-level components of a project or workflow. DocOps Box is optimized for an AsciiDoc, YAML, and Liquid tech stack.
- toolchain
-
A set of software tools that work together to accomplish a task. In the context of DocOps Box.
- container
-
A running instance of a Docker image. The container is isolated from your host system but can read and write files through a bind mount. When the container stops, any changes made outside of mounted paths or named volumes are discarded.
- named volume
-
A persistent storage area managed by Docker, separate from both your project directory and the container’s own filesystem. DocOps Box uses named volumes to store installed dependencies (Ruby gems, Node packages, Python packages) and your command history, so they survive container restarts. Unlike a bind mount, a named volume is invisible on the host; its contents are accessible only from inside a container.
- bind mount
-
A direct link between a directory on your host and a path inside the container. DocOps Box bind-mounts your project directory to
/workspaceso your files are accessible inside the container without copying. Changes on either side are immediately reflected on the other.
- Docker Hub
-
Docker’s public registry for sharing and downloading container images. DocOps Box pre-built images are hosted there under the
docopslaborganization. When you pull an image for the first time, Docker Hub is the source.
- registry
-
A server that stores and distributes Docker images. Docker Hub is the default public registry. The
IMAGE_REGISTRYvariable lets you specify a private or alternate registry.
- image tag
-
The label appended after the colon in an image name. For example, the
workindocopslab/box-max:work. Tags identify the specific variant, version, or configuration of an image.
- Docker Compose
-
A tool for defining and running Docker containers using a YAML configuration file. DocOps Box uses a minimal Compose file (
docopsbox.yml) so that container configuration (volumes, environment variables, user IDs) is version-controlled and shareable across a team.
- host / host system
-
Your computer’s own operating system and filesystem; the environment you are in before entering a container. Commands like
dxbx upare host commands. Commands likebundle installrun inside the container, not on the host.
- shell
-
A program that accepts text commands and passes them to the operating system to execute. Bash and Zsh are both shells. When you open a terminal, you are running inside a shell. DocOps Box
workimages use Zsh with OhMyZsh;liveimages use Bash.
- PATH
-
An environment variable that lists the directories your shell searches when you type a command name. If
dxbxcannot be found after installation, your PATH does not yet include the installation directory:${XDG_BIN_HOME}(which defaults to~/.local/binifXDG_BIN_HOMEis not set).dxbx installwill offer to add the required line to your shell profile automatically.
- UID / GID
-
Representing user identifier and group identifier, numbers the Linux kernel uses to track file ownership and access permissions. Every file belongs to a UID and a GID. If files the container writes appear as owned by an unexpected user, set
HOST_UIDandHOST_GIDin.config/.env.localto match the output ofid -uandid -gon your host.
- SSH / SSH agent
-
SSH (secure shell) is the protocol used for encrypted communication between computers, including authenticating to GitHub for pushing and pulling code. The SSH agent is a background process that holds your decrypted SSH keys so you do not have to type your passphrase repeatedly.
Executing
dxbx upautomatically forwards the host SSH agent into the container whenSSH_AUTH_SOCKis set in the host shell. SSH agent forwarding in VS Code Dev Containers requires per-OS configuration; see the comments in.devcontainer/devcontainer.json.
- entrypoint script
-
A script that runs automatically each time a container starts, before the main process. DocOps Box’s entrypoint detects the
HOST_UIDandHOST_GIDenvironment variables and adjusts the container user’s identity to match, ensuring files you create inside the container are owned by you on the host.
- postCreateCommand
-
A VS Code Dev Containers configuration hook that specifies a command to run automatically the first time a container is created. DocOps Box uses it to run
bundle install,npm install, andpip installso declared dependencies are ready immediately after the container starts, without any manual step.
- runtime / runtime environment
-
The software layer responsible for executing programs written in a specific language. Ruby, Node.js, and Python each have a corresponding runtime that must be installed to execute programs written in that language. DocOps Box images bundle the runtimes you need so you do not have to install them individually on your host.
- dependency manifest file
-
A document that declares a project’s dependencies and (optionally) the specific versions required. Examples:
Gemfile(Ruby),package.json(Node.js),requirements.txt(Python). A dependency manager reads the manifest to install exactly the right packages.
- dependency manager / package manager
-
A tool that reads a manifest file and installs the listed packages at compatible versions. Examples: Bundler (
bundle install) for Ruby, npm for Node.js, pip for Python. In DocOps Box, installed packages are stored in named volumes so they survive container restarts.
- gem
-
A Ruby software package, distributed via RubyGems and managed by Bundler. When you run
bundle install, Bundler reads yourGemfileand installs the declared gems into the project’s named volume.
- version manager
-
A tool that allows multiple versions of a runtime (Ruby, Node.js, Python) to coexist on a single machine and be switched per project. Examples: rbenv and RVM for Ruby; nvm for Node.js; pyenv for Python. DocOps Box handles versioning through its pre-built images. Version managers are only relevant if you install runtimes directly on the host.
- CI/CD
-
Stands for continuous integration/continuous deployment (or delivery), an automated pipeline that builds, tests, and optionally publishes your project every time you push/merge changes into the main branch. DocOps Box
liveimages are designed for use in CI/CD environments.
- CLI
-
Short for command-line interface. Refers to any prompt-based program you interact with by typing commands into a terminal, as opposed to clicking through a graphical presentation. Most tools in the DocOps ecosystem are CLI tools. CLI is effectively a superset which includes TUI applications.
- TUI
-
For text-based user interface. An application that runs inside a terminal but presents a structured, screen-filling layout; more than a plain command prompt, less than a graphical window. The text editors included in DocOps Box images, nano and Vim, are TUI applications.
- OAS / OpenAPI Specification
-
A standard machine-readable format for describing or defining REST APIs (formerly named Swagger). OAS documents (OAD) are typically written in YAML and used to generate API documentation, client libraries, and testing regimens.
- IDE
-
Stands for integrated development environment, an application that combines a code editor with tools for navigating, building, and debugging software. VS Code is the IDE recommended by DocOps Box; it integrates with Docker through the Dev Containers extension.
The following tools are available inside every *max:work image:
| Tool | Purpose | CLI |
|---|---|---|
Converts |
|
|
Converts |
|
|
Parsees and manipulates HTML and XML |
|
|
Renders ERB, Liquid, and many more template formats |
|
|
Document conversion/migration |
|
|
Linting and style checking |
|
|
OpenAPI validation and docs generation |
|
|
OpenAPI validation and mock servers |
|
|
OpenAPI document manipulation |
|
|
Document conversion and manipulation |
|
|
Command-line JSON processor |
|
|
Command-line YAML processor |
|
These are container-shell commands.
From your host terminal, use dxbx up to start an interactive container session and then invoke tools at the prompt, or use dxbx ex <command> to run a single tool directly without entering the container shell.
.adoc files under docs/ into build/asciidoctor -R docs -D _build docs/**/*.adoc
asciidoctor -a toc=left -a sectnums \ -a source-highlighter=rouge -o _build/manual.html \ README.adoc
asciidoctor-pdf -a toc -a pagenums -o _pubs/manual.pdf manual.adoc
kramdoc -o README.adoc README.md
Use Nokogiri’s CLI mode to parse and manipulate HTML and XML documents.
nokogiri https://docopslab.org/docs/contributing/ \
-e 'puts $_.css("h1,h2,h3").count'
nokogiri https://docopslab.org/docs/contributing/ \
-e '$_.css("a[href]").each { |a| puts "#{a.text.strip}\t#{a["href"]}" }'
Use Tilt’s CLI mode to render templates in various formats.
echo 'Hello, <%= name %>!' | tilt -t erb --vars='{name: "world"}'
echo 'Hello, {{ name }}!' | tilt -t liquid --vars='{name: "world"}'
tilt --list
The Redocly CLI provides utilities for working with OpenAPI documents.
redocly lint api.yaml
redocly build-docs api.yaml -o _build/api-docs.html
The LibreOffice tooling in work containers is primarily intended for use in CI/CD pipelines, where you can invoke the live image directly with docker run or via dxbx ex to perform conversions and other operations on demand.
|
|
This section is entirely generated using Lumo LLM, and has not been verified for accuracy. My clients sometimes need these tools, but I have not worked with them. |
| Feature | soffice | unoserver |
|---|---|---|
Document opening/creation |
✅ Full support |
❌ Not directly |
File conversion |
✅ |
✅ Via server API |
Print operations |
✅ |
❌ Not directly |
Headless mode |
✅ |
✅ Implicit (daemon mode) |
Network/API access |
|
✅ Built-in XMLRPC/UNO servers |
Daemon/background mode |
❌ Limited |
✅ |
Request limits |
❌ No |
✅ |
Conversion timeouts |
❌ No |
✅ |
Logging control |
✅ |
✅ Same options |
Port configuration |
❌ Manual via |
✅ |
| Operation | soffice | unoserver |
|---|---|---|
Print to PDF |
|
❌ |
Batch processing |
Looping |
|
CI/CD pipelines |
|
✅ |
Scripted migration |
✅ |
|
-
Converting files in a simple script (
--convert-to pdf *.doc) -
Printing documents directly
-
Running occasional batch operations
-
Executing macros on specific files
-
Quick one-off document operations
-
A persistent service handling multiple conversion requests
-
Centralized logging and monitoring
-
Timeout protection for stuck conversions
-
To build an application that programmatically calls LibreOffice
-
To limit resource usage (
--stop-after,--conversion-timeout) -
Network-accessible document conversion
sofficesoffice --headless --convert-to pdf input.doc
soffice --headless "macro:///Standard.Module1.MyMacro(input.doc)"
soffice --headless \ --convert-to odt \ --outdir /workspace/output \ /workspace/input/*.txt /workspace/input/*.docx
unoserver \ --interface 127.0.0.1 \ --uno-interface 127.0.0.1 \ --port 2003 \ --uno-port 2002 \ > /workspace/unoserver.log 2>&1 &
unoconvert \ --host 127.0.0.1 \ --port 2003 \ --convert-to pdf \ /workspace/test_in/sample2.docx \ /workspace/test_out/sample2.uno.pdf
kill "$UNOSERVER_PID" wait "$UNOSERVER_PID" 2>/dev/null || true
Are you ready to install Ruby and other tools on your workstation or another host so you can stop messing with Docker and dxbx?
|
💡
|
If you already know you want to install some or all software directly to your host, skip to the nuts and bolts. The next few sections are for helping you decide. |
Whatever your operating system, you are going to have to roll up your sleeves and take several steps. Over time, you will also need to actively configure and periodically upgrade most of what you install here; this is the reality of maintaining a “development environment” on your workstation.
|
📎
|
Windows users are still advised to perform Ruby/runtime-centric commands on Linux via WSL2, so these instructions focus on that approach rather than a native Windows install. |
Zsh and Git should be more straightforward, if you are not already using them directly on your host workstation.
Ruby can be a bit trickier, but my clients have found this method not to be too frustrating on both MacOS and Linux, including Linux via WSL2 on Windows.
Node.js, Python, and Pandoc are also relatively simple to install and set up, and all three are very likely to be required or at least helpful in your career using developer tools to manage documents and documentation.
For these reasons, you may be well advised to “bite the bullet” and install everything directly on your host, skipping or abandoning Docker, or using it case by case.
|
❗
|
Host-side installation does not preclude concurrent usage via Docker. You can use pretty much any mix of native installs and Docker-based fallbacks. |
Then again, maybe this is more than you wish to manage.
Let’s go over why you might not want to move away from the Docker method.
If the Docker method is working for you, there may be no real reason to switch.
The Docker method might be almost necessary where lots of frequently changing dependencies are being managed.
In fact, if you are already working with a modified docopsbox.yml shared among multiple users, you are probably experiencing the advantages of standardizing around a shared container toolchain: containers that start identically every time, with common commands.
If you are working with someone who is constantly developing Ruby-based tools, they are responsible for helping keep your environment up to date. Especially across multiple runtimes (mixing Python, JavaScript, or who-knows-what in a larger project), containerization may be the only way to keep up.
My own work should not require many changes in this regard, so if you’re following my advice longer term, at least in my case, the whole point is to hopefully keep the dxbx script/box images combination relevant and useful to all my clients.
If the work you’re doing depends entirely on Ruby and shell commands, the Docker method offers less advantage, even though that is also foremost what it is designed for.
|
💡
|
Having Git installed on your workstation (host) probably does have big advantages and really no downside. Having it installed on your host will not conflict with the Git installation in the DocOps Box Docker image, either. |
If you are using multiple Docker containers or a heavily adapted version of dxbx, it will likely be harder to reproduce and maintain what Docker does for you currently.
In short, if you have not been told to install Ruby, Node.js, Python, Pandoc, and the like directly to host, you probably do not need to.
|
💡
|
If you find yourself comfortable using containers run through dxbx, you may want to explore the docker and docker compose commands and the broader Docker “ecosystem”.
Some people maintain elaborate development environments in Docker and install very little on their host workstation.
|
Of course, no harm should be done if you set things up locally and invoke Docker containers as a fallback. The remainder of this appendix is for those who want to set everything they need up on their work machine and any live instances (servers, CI/CD) directly.
Some tools work best when installed directly on your host machine rather than inside the container. Having them available on your host can improve performance, integration with other software, and ease of use.
Similarly, tools that need to affect your host environment outside a particular project directory, simply need to be installed on the host.
This guide advises installing the following tools directly on your host system, even if you leave others to DocOps Box containers.
- Git
-
DocOps Box does its best alias a proper host installation, but it does better when there is a proper host installation. Git is one of the easiest tools to “maintain” and one of the handiest to have installed directly on your host.
- Zsh
-
MacOS users already enjoy Zsh as their default shell, and Linux users can easily install it with their package manager.
- VS Code integrations
-
Language servers, linters, and formatters that VS Code invokes while you type (ESLint, Prettier, the Vale extension’s binary). Install these directly according to their own documentation so your editor can find them.
- Tools you call constantly from a terminal
-
If you find yourself typing
dxbx ex asciidoctor …ordxbx ex pandoc …dozens of times a day, it is probably time to install those tools on your host. Pandoc has minimal host-side dependencies and installs cleanly on MacOS, Linux, and WSL2 without a version manager.Asciidoctor is better installed through a proper runtime platform, such as Ruby, Node.js, or JVM. This guide recommends Ruby and Node.js.
Creating an alias command in your host shell that invokes these tools via container
alias pandoc='dxbx ex pandoc'can be a good middle ground if you don’t want to install them directly on your host but want to avoid typingdxbx exevery time. Frankly,dxbx exis kept as simple as possible to make this less of an issue.
Having a tool installed both on the host and inside the container causes no conflict.
The container’s PATH🔖 is entirely separate; each invocation independently resolves to whichever install is in scope.
If you have decided it is worth the trouble to install the software directly and locally, this is your guide to minimizing overhead.
Advice on the few tough choices involved and some maintenance tips are included.
It is perfectly possible to run all of these programs and environments, and much more, under WSL2 on Windows 10 or 11. This is strongly recommended as the way to maintain an optimal, consistent environment. Skip to the Linux guide to get started.
- Truly Native Windows Dev Environment
-
Alternatively, you can get all of this stuff to run directly on Windows, though I do not recommend that route.
It has become especially possible to run a proper development environment on Windows 10/11, particularly with the GitBBash program that ships with Git for Windows.
Ruby has a Windows installer (choose the latest x64 with Devkit), and it’s supposedly even possible to install Zsh on Windows.
It is far from irrational to install everything on Windows, but it’s also probably not optimal, since WSL2 works so well and aligns your system with the absolute vast majority of professional and open-source coders (including anyone with a Mac).
Because this path is not recommended, this guide will not instruct it. However, the links above are a good place to start.
Otherwise, stick with WSL2, and skip to the Linux guide.
Mac users will likely have the fewest steps to get everything installed, especially if you already have Homebrew set up.
|
💡
|
Homebrew is an essential resource on MacOS. It is the equivalent of an official package manager in Linux distributions, managing dependencies and easing the update process. It also keeps you out of Apple’s sphere of control when it comes to installing and maintaining CLI tools and even GUI apps like VS Code, Slack, Postman, LibreOffice, and more. |
MacOS ships with Zsh, and you are probably already using it. I recommend further customizing with OhMyZsh, but otherwise no action is needed.
Likewise, MacoS ships with Git.
Try the command git --version to ensure that it is installed.
If not, Homebrew to the rescue!
brew install git
Even though OSX comes with Ruby pre-installed, it is not properly set up for our purposes, and you will likely see lots of errors and be forced to use the sudo command prefix to perform commands as superuser, which is not advised.
Instead, use rbenv or another Ruby management platform, as instructed below.
Install any packages except Ruby using your package manager.
For example, on Ubuntu- or Debian-based distributions, you can install Zsh and Git with:
sudo apt-get install zsh git
Your distribution likely does not come with Ruby installed, and we do not advise using your package manager to install it. This is likewise true for Node.js.
Most Linux distributions ship with Python 3 installed. See the section in this guide on Python for instructions on managing Python versions if needed.
Even if your operating system already has Ruby installed, you should reinstall with a version manager. This is especially the case with MacOS, which has weird permissions issues with its native installation.
My personal version manager of choice is rbenv. I have never seen rbenv installation fail using their recommended procedure.
If you are feeling adventurous, rbenv’s maintainer keeps this list of alternative Ruby managers, with good things to say about several of them. I may get around to experimenting with them, but for now my instructions will assume rbenv.
The default version in the accompanying Docker image is 3.3, which is well-supported across the gem ecosystem. The latest supported version (3.4) can also be used; choose it for new projects.
With rbenv or an equivalent version manager you can keep multiple versions on hand for projects with divergent dependencies. In that case, set different local versions:
rbenv install 3.3 rbenv install 3.4 rbenv global 3.4 cd project-a rbenv local 3.3 cd ../project-b rbenv local 3.4
All executions of ruby, bundle exec, etc. will use the locally appropriate Ruby stack.
This approach is compatible with the DocOps Box/dxbx method, which uses the right Docker image per project.
Once Ruby is installed, you can install any gems you like globally on your host system.
The ones that ship with DocOps Box work images are:
gem install asciidoctor kramdown-asciidoc nokogiri tilt
If you decide not to use the Docker image, you may still wish to use the optional software included in the image. Here we instruct setting up the other runtimes and utilities that are included in the DocOps Box Docker image.
Whether you use Node.js as a main runtime environment or not, you will sooner or later surely need the Node Version Manager (nvm) application to manage Javascript assets.
Both nvm and Node.js are best installed using their platform- and installer-specific documentation. Be sure to choose your platform (Linux or MacOS). For the rest, leave default settings, unless you have reason to do otherwise.
|
📎
|
Windows users should definitely install these resources on their WSL2 hosting instance, even though there are Windows versions available. |
As of now, the Version 24 line is most widely used, including by the DocOps Box images.
If you need multiple versions of Node.js, that’s what nvm is for.
Just use commands like nvm install 22 and nvm use 22 to switch between versions.
Most Linux and MacOS distributions come with Python 3 pre-installed, including the Ubuntu and Fedora distributions that work with WSL2 on Windows.
In my experience, the pre-installed Python versions are usually sufficient for the tools we tend to use for docs-as-code. If you need to install or manage Python versions, the pyenv tool is a good choice, with installation instructions for all platforms.
Similarly to Ruby on MacOS, the pre-installed Python on Linux may have permissions issues that make it difficult to work with.
Even if Pandoc is not central to your documentation toolchain, sooner or later it will be just the right tool. It can be especially useful during one-time migrations from one source format to another.
|
📎
|
It may be more sensible to install Pandoc directly on Windows in addition to WSL2, just for ease of access. |
Pandoc maintains downloads and installation instructions for all operating systems.
Vale is a prose linter that can be configured with style guides to enforce writing standards.
Follow Vale’s installation instructions for your platform to set it up natively.
To take advantage of any of LibreOffice’s document manipulation tools or extensions, install the CLI tools.
Support for LibreOffice functionality on the command line comes in two separate tools: soffice and unoconv/unoserver.
Unfortunately, installation of these tools is complicated.
Look to the DocOps Box Dockerfile for reference, but novice users may need help with this step.
The unoserver command relies on Python, and the soffice command comes with LibreOffice utilities and may already be available or may need multi-step installation.
Redocly CLI is a powerful tool for managing OpenAPI documents, including generating API references from them. It runs on your Node.js runtime, so you can install it with npm:
npm install -g @redocly/cli
The DocOps Box work images include a handful of quality-of-life tweaks beyond the default Zsh/OhMyZsh setup.
If you are running these tools on your host, you can reproduce any of these you find useful.
- Shell: command aliases
-
Add to your
~/.zshrc(or~/.bashrc):alias edit=nano alias ls="ls -lha --color=auto" # (1) alias cat="bat --paging=never" # (2) alias grep="grep --color=auto" alias egrep="egrep --color=auto" alias jekyllserve="bundle exec jekyll serve --host=0.0.0.0"
-
lswith human-readable sizes, hidden files shown, and colorized output. -
batis a syntax-highlighting wrapper forcat; install natively
-
- Shell: directory navigation
-
Add to
~/.zshrcto enablecd-free navigation and a pushd stack:setopt AUTO_CD setopt AUTO_PUSHD setopt PUSHD_IGNORE_DUPS
- Shell: case-insensitive tab completion
-
Add to
~/.zshrc:zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
- Git defaults
-
Sane defaults for everyday workflows. Run:
git config --global init.defaultBranch main git config --global pull.rebase false git config --global push.autoSetupRemote true git config --global core.pager cat
- nano settings
-
Create or edit
~/.nanorc:set linenumbers set mouse set tabsize 2 set softwrap
- Vim settings
-
Create or edit
~/.vimrc:syntax on filetype plugin indent on set number
This project emerged from numerous attempts to provide robust runtime environments for clients with complex needs and insufficient capacity to learn or maintain all of these platforms.
Truth be told, many or even most developers cannot or wish they did not have to maintain complicated development environments on their local machines. This is especially true for technical writers and other documentation professionals, who tend to have less experience with and appreciation for command-line tools, package managers, and recursive dependency chains.
Short of being a turn-key/magic-wand solution, DocOps Box is meant to be a one-stop-shop, or maybe a first-stop-shop, for getting “up and running” with what I think most people will need to perform powerful tasks with digital documents of various kinds.
There will inevitably be further installation and configuration steps to perform before you’re automating all your document operations. This project intends to get you to that point without much knowledge of Linux, Ruby, Node.js, Python, Git, SSH, or even Docker, for that matter.
The main purposes of technologies such as Docker, Compose, and Dev Containers are to facilitate ready-made environments that are consistent across teams and throughout software-production and -delivery pipelines. In this sense, they are purpose built for some expert to design/configure them to fit a certain situation that may be useful to dozens or thousands of non-experts who just need to perform tasks in a reliable state.
I spent years watching my clients (mostly technical writers on Windows) struggle to get Ruby and its dependencies installed so they could work locally with the platforms I assembled for them.
Docker genuinely fulfils the promise of containerization, providing just-right environments that unify a team around standardized setups. But getting there on your own means choosing the right base image, wiring up volumes, user ID mapping, environment variables, SSH key handling, and a compose file that everyone can actually use.
All of that adds up fast. Without experience and context, even using LLMs (“AI”) to generate Dockerfiles and compose files can lead to very messy results that only compound when shared with a team.
And suddenly all the docker build and docker run commands you need to execute to get going are so complex that they are more of a barrier than the original problem of installing the environment on your host machine.
This is why DocOps Box includes configuration files and a dxbx executable.
At least for getting your environment ready, commands should be extremely simple, with all the complexity of Docker and even Compose abstracted away.
DocOps box attempts to expand this per-application approach to something like a per-domain approach, where the domain is “document operations” for multiple projects or distinct sets of documents. It does not promise to be “all things to all users”, but it does make some big promises. One is that this project is more convenient to new users than the alternatives.
To be certain, let’s explore those alternative means of “#bootstrapping”🔖 a document operations environment, and how we hope to ensure this project is a better way to get up and running.
- native installation instructions
-
There are advantages to native installation of all the tools you need, but it is also the most time-consuming and maintenance-heavy approach. The
READMEfile instructs how to install and configure the necessary tools directly on each user’s host machine. This approach tends to require the most active maintenance, and of course it takes users the most time to set up.See the Installing Everything to Host section for instructions on installing Ruby for an example of how much documentation might be needed just for setup instructions for each project.
- virtual machines
-
Setting up a virtual machine with tools like VirtualBox or Vagrant can provide an isolated environment, but it requires more resources, a steeper learning curve, and more maintenance than Docker containers. VMs are also not ideal for CI/CD pipelines, cloud-based development environments, or quick one-off tasks.
- Codespaces
-
Cloud-hosted development environments like GitHub Codespaces can be configured with a
.devcontainerdirectory in your repository. While Codespaces offers a seamless experience for users with GitHub accounts, it is not universally accessible, especially for those working in private repositories or without GitHub accounts.Codespaces is also not a local environment, and honestly I just cannot advise working in a cloud system because I cannot fathom working that way myself. It is possible that cloud-first is a better way to learn docs-as-code tools and workflows, but I have not even heard this claim, and I would not be the person to lead such a project.
If anyone knows of other approaches that might be better for providing such a broad swath of technologies in a ready-to-use way, I would love to hear about it.
The dxbx script always shows you the exact Docker commands it runs, which has the handy side effect of teaching you the underlying tooling if you care to learn it.
If you advance to the point where docker compose commands feel natural, you can use them directly; nothing about DocOps Box locks you in.
It is common to hear Ruby described as somewhat in decline as a programming language. Fortunately, its vast and well-maintained library of tools (typically packaged and published as “gems”) is still in widespread use, and the community is alive and well.
Ruby is the basis for countless excellent command-line tools and APIs, including Asciidoctor, Liquid, Jekyll, Nokogiri, and Guard. Additional tools that are not Ruby-native but have excellent Ruby APIs include Pandoc (via pandoc-ruby) and ImageMagick (via rmagick).
I make no claim that Ruby is somehow the ultimate runtime environment for document automation. Node.js and Python are strong contenders, and a truly complex docs-management system may of necessity incorporate more than one runtime.
Nevertheless, the main thread of my software preferences is Ruby-native, and DocOps Box is directly aimed at getting people up and running with those tools.
The max images carry Node.js and Python alongside Ruby precisely so the environment does not become a dead end if your toolchain requires them.
These images are suitable for and tested with numerous Node.js and Python tools.
Ever since Apple made Z shell the default shell on MacOS, I have felt confident recommending it to anyone needing a terminal shell. As a superset of Bash, Zsh users can always run Bash scripts and commands without conflict.
Zsh provides a noticeably better user experience out of the box, and OhMyZsh builds on it with sensible defaults:
-
Tab autocompletes commands, filenames, and even CLI arguments.
-
↑ / ↓ cycles through your command history; Ctrl+R searches it.
-
Prompt syntax highlighting shows you whether a command is recognized before you press Enter.
These features are technically achievable in Bash with enough plugins and configuration, but Zsh and OhMyZsh make them trivially available from day one.
The only downside: you may miss them when stuck at a bare server Bash prompt, which is exactly why live images stay on Bash, where CI/CD environments expect it.
- Why Docker + Compose?
-
Raw
docker runcommands quickly grow unwieldy: volumes, ports, user IDs, environment variables, image tags, all of them manually specified each time. Docker Compose externalizes all of that into a versioneddocopsbox.yml, making the run configuration reproducible and shareable in Git.Fortunately, the breadth of DocOps use cases does not require a complex multi-service setup, so the Compose file is simple and approachable for users new to Docker.
- Why separate
workandlivecontexts? -
Interactive daily work benefits from things that automated pipelines do not, such as Z shell, TUI text editors, and full terminal tooling. Including these in a
liveCI/CD image wastes build time and inflates image size. Separate contexts keep each image lean and purpose-built.That said, you could also use one
max:workimage for both purposes early on if you don’t wish to fuss about the distinction. - Why named volumes for all dependency caches?
-
Dependency trees for Ruby, Node.js, and Python all produce Linux-native artifacts (compiled gem extensions, Node binary modules, Python virtualenv symlinks) that are wrong-architecture or broken when accessed from the host filesystem on MacOS or Windows. Keeping them in named volumes (invisible to the host) removes that failure mode entirely and eliminates bind-mount overhead on MacOS. Keeping dependency files invisible to the host filesystem also keeps searches cleaner than if they were stashed in a directory under the project root.
Dependency caches are disposable and fully reproducible
bundle install,npm install, orpip installregenerates them in minutes. - Why an entrypoint script?
-
Docker images deployed to a registry cannot know the UID or GID of the user who will run them. Placing UID/GID reconciliation in the image’s own
ENTRYPOINTscript (rather than baking it at build time or requiring a separate file in the user’s project) satisfies all three requirements simultaneously: it runs on everydocker runordocker compose runinvocation, works with pre-built registry images, requires no extra files in the user’s project directory.The entrypoint detects the
HOST_UIDandHOST_GIDenvironment variables (passed in bydocopsbox.yml), adjusts the container user’s identity to match, then drops privileges and executes the original command ($CMDor the default shell). On MacOS with Docker Desktop, the VM layer provides transparent ownership mapping and the entrypoint is a no-op (it does nothing and throws no error). - Why no [your runtime here]?
-
The
maximages include Ruby, Node.js, and Python because they are the environments I repeatedly find myself using and recommending to clients. Tempted as I am to add Java, Go, and Rust, I have no real experience supporting these platforms and only sporadic need. Go, Haskell, and Rust applications typically compile to static binaries that run just fine on the host, such as Vale and Pandoc. Adding more runtimes would also bloat the image size and build time, and I want to keep the project focused on documentation operations rather than becoming a general-purpose development environment.
DocOps Box is maintained by DocOps Lab. Contributions are welcome.
-
Clone the repo.
git clone git@github.com:DocOps/box.git
-
Build a permutation of the image.
Exampleworkimage build with specific Ruby versiondocker build \ --build-arg IMAGE_CONTEXT=work \ -t docopslab/box-max:work \ .
For now, execute these sample commands to make sure your new image is working properly.
docker run --rm docopslab/box-mine:work ruby --version docker run --rm docopslab/box-mine:work bundle --version docker run --rm docopslab/box-mine:work git --version docker run --rm docopslab/box-mine:work pandoc --version docker run --rm docopslab/box-mine:work npm --version docker run --rm docopslab/box-mine:work python3 --version
See our Contributors Guide for general policy and instructions.
Main ways to contribute:
-
Open an issue for bug reports or feature proposals.
-
Fork the repository and submit a pull request.
Contributions should:
-
Not break the
min:liveCI/CD use case. -
Not increase
maximage size unnecessarily. -
Update this README alongside any user-facing changes.
-
Follow the AsciiDoc authoring conventions used throughout this document.