About

Sphinx is a documentation generator which is based on a plain text format for its sources. Typically it uses Restructured Text as its source format, thanks to a plugin system, other formats like Markdown can be used as well.

Sphinx allows to render the documentation into various formats. The PDF output is based on LaTeX and at times somewhat tricky to set up. In this post I describe a fairly lightweight setup which I found based on the combination of Nix and Docker.

Chosen approach

For this need, I wanted to have a container which is targeted for the usage on a developer's machine and it should be easy to build it locally and to customize it if needed. Based on this I decided to avoid the need to have a local setup of Nix for building the container image.

Instead of using Nix' builtin support to generate images for Docker containers, I used a minimal based image which does provide Nix inside of the container, so that I could use it like a regular package manager to install the needed dependencies.

Due to the flexibility in Nix' approach when it comes to customization, I did hope to end up with a fairly small container image which should be also quick to build locally.

Original approach

My original attempt was based on using an existing container specification which the Sphinx project uses for its CI setup out of the repository docker-ci.

With a few tweaks I did end up with a container which allowed to build HTML and also PDF results fairly well. It did have two drawbacks though: The image size was in the range of 2.5 GiB and the time to build the image was in the range of half an hour.

This did not seem to be a good approach in the current time, when people seem to give any approach up to max. 5 minutes to produce some results to work with. This drove me to give a Nix based approach a try.

Found results

The results where quite surprising.

The size of the container image dropped to around 400 MiB and the time to build the image got into the range of about 2 minutes.

For my personal taste I would still like to get the image size further reduced, still I find this is something which one can sufficiently work with.

Technical details

Base image

I found that the image lnl7/nix:2018-03-13 serves well as a base. It has been prepared by LnL7, the sources can be inspected in his Github respository nix-docker and also allow to build and customize the base image on your own (if you want).

FROM lnl7/nix:2018-03-13

Dockerfile

The Nix specific part consists of two steps:

  1. Copy the environment definition file into the container.
  2. Install it via nix-env, so that the packages are available in the container.

I found that running the garbage collector at the end does nicely reduce the final image size.

COPY sphinx-env.nix /tmp

RUN nix-env -f /tmp/sphinx-env.nix -i \
  && nix-env -iA nixpkgs.gnumake \
  && nix-collect-garbage -d

The other fragments are specific to Sphinx and the extension sphinx-autobuild:

RUN mkdir /source
WORKDIR /source

EXPOSE 8000/tcp

CMD sphinx-autobuild \
  --host 0.0.0.0 \
  --port 8000 \
  --ignore '.git/*' \
  --ignore '.*' \
  . _build/html

Nix environment definition

I started by creating a Python environment which contains Sphinx and adds m2r for Markdown support and sphinx-autobuild to make the local workflow nicer by having automatic HTML build being served up in the container:

sphinx-env = python.withPackages(ps: [
  ps.sphinx
  ps.m2r

  sphinx-autobuild
]);

To allow for PDFs to be generated, I also added a customized version of Texlive and combined both into a regular environment:

full-sphinx-env = pkgs.buildEnv {
  name = "full-sphinx-env";
  paths = [
    sphinx-env
  ] ++ (pkgs.lib.optional withPdf latex);
};

Compose specification

docker-compose proved to be especially useful when using containers to run commands in isolation. It allows to nicely specify mappings for ports and volumes, so that the usage is very convenient:

version: '3'
services:

  build:
    image: sphinx-build
    build:
      context: _services
      dockerfile: build.df
    volumes:
      - .:/source
    ports:
      - "8000:8000"

Typical usage

Have sphinx-autobuild available serving the HTML version:

docker-compose up build

Building HTML or PDF in the container:

docker-compose run build make pdflatex
docker-compose run build make html

Comments

comments powered by Disqus