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:
- Copy the environment definition file into the container.
- 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