I recently fancied building Firefox locally to have a go at fixing a bug I’d encountered using it. Although installing all of the dependencies for Firefox is very straightforward, I generally try to keep my machine free of too much clutter I don’t need, so I figured this would be a good time to get started learning about Docker.

Writing the Dockerfile

Scroll down to see the finished Dockerfile or view it here.

We’ll start by writing the Dockerfile needed to create the image. I’ve used ubuntu:17.04 as the base OS image and set the maintainer label as well.

FROM ubuntu:17.04
LABEL maintainer="James Turner"

From here we’ll set the default command that will be executed when the image is started (provided no commands are passed in to override it at runtime).

mach is a CLI tool used for compiling, running and testing Firefox. The default behaviour of the image will be to begin compiling when we hit run, so we’ll use the build command as default.

CMD ["./mach", "build"]

./mach build complains if you don’t have your SHELL environment variable set, so let’s add that.

ENV SHELL /bin/bash

Installing dependencies

Mozilla kindly maintains a nice python bootstrap script to install everything we need to build Firefox from source. But to download and run that we’ll need wget and python (remember we have next to nothing installed by default when we start with our base OS image).

Always run the apt-get update and install on the same RUN line in the Dockerfile, otherwise Docker will cache the build of the image after the update and when it comes to the install the package lists might not necessarily be up-to-date. See Dockerfile best practices.

RUN apt-get update && \
    apt-get install -y wget python

Now let’s download and execute that bootstrap script. We’ll call it with the --no-interactive flag so that it will run without question prompts.

RUN wget -q https://hg.mozilla.org/mozilla-central/raw-file/default/python/mozboot/bin/bootstrap.py -O /tmp/bootstrap.py
RUN python /tmp/bootstrap.py --application-choice=browser --no-interactive

I found that you also need to manually install clang and llvm. For some reason they don’t seem to get installed by the bootstrap script, so we’ll add those to the apt-get install above.

Another issue we need to fix is that the rust compiler isn’t added to the PATH by default, so we’ll set that variable as well in the Dockerfile.

ENV PATH="/root/.cargo/bin:${PATH}"

Finally we’ll create the directory where our source code will sit, setting it as our working directory.

RUN mkdir -p /usr/local/src/firefox
WORKDIR /usr/local/src/firefox

By now, the Dockerfile should look something like this:

FROM ubuntu:17.04
LABEL maintainer="James Turner"

CMD ["./mach", "build"]

ENV SHELL /bin/bash

ENV PATH="/root/.cargo/bin:${PATH}"

RUN apt-get update && \
    apt-get install -y wget python clang llvm

RUN wget -q https://hg.mozilla.org/mozilla-central/raw-file/default/python/mozboot/bin/bootstrap.py -O /tmp/bootstrap.py

RUN python /tmp/bootstrap.py --application-choice=browser --no-interactive

RUN mkdir -p /usr/local/src/firefox

WORKDIR /usr/local/src/firefox

Building the image

$ docker build -t firefox-dev:latest .

The source code

In order to be able to manage the source code and use our favourite editor, we’ll download it to the host and use a Docker volume to mount the directory inside the container.

$ hg clone https://hg.mozilla.org/mozilla-central/ ~/firefox-src

Building Firefox

Now that we have the source, it’s time to run the image…

The -v flag sets up the volume to map between the source code’s directory on the host and /usr/local/src/firefox on the guest.

$ docker run -v /home/james/Projects/firefox:/usr/local/src/firefox -it firefox-dev:latest

It might take a while but eventually the compile will complete and we’ll have our build.

Running the build

We can now run Firefox inside the container as well. We’ll override the CMD to be ./mach run and pass through the $DISPLAY variable to the container as well as mount the unix domain socket in order to grant the container access to the X server on the host.

$ docker run -v ~/firefox-src:/usr/local/src/firefox -v /tmp/.X11-unix:/tmp/.X11-unix -it -e DISPLAY=$DISPLAY firefox-dev:latest ./mach run