
Running Firefox Nightly in a container
This post explains how to run GUI applications in containers, using Firefox Nightly as an example.
Recent trends in web browsers
My primary browser on desktop these days is Waterfox, installed via Flathub, and it also has an Android app. I like that these have AI-related features disabled by default. Perhaps I’m just an old-fashioned person who can’t keep up with human progress, but for now, I still can’t bring myself to like the situation where AI features are being shoehorned into everything.
Ladybird is another interesting candidate worth mentioning, but as it’s in a pre-alpha state, it doesn’t seem suitable for everyday use yet.
There is an image format called JPEG XL. After Chromium dropped support for it, they recently re-added it, whereas Waterfox already supports it. Looking at the support matrix (Can I use…), among widely used browsers, it seems that only Safari currently supports JPEG XL. Before building this blog, I decided to check whether the image format displays correctly in Firefox.
Back in 2023, there was an announcement that an APT repository providing Firefox Nightly had been set up. And just a few weeks ago, there was a post stating that packages for RPM-based systems had also been provided. However, as expected, it hasn’t been packaged for the musl-based Void Linux I use. Building the browser locally seemed a bit of a hassle, so I decided to run it in a container instead. I’ll explain the steps in the next section.
Run a graphical browser with sound using podman
Prerequisites
The following assumes that Podman is used for container management and that the podman-compose command is available.
podman compose versionpodman-compose version 1.5.0podman version 5.7.1As this assumes the use of a Wayland compositor, the environment variables XDG_RUNTIME_DIR and WAYLAND_DISPLAY must be set as shown below. The output may differ from the example.
printf '%s\n' ${XDG_RUNTIME_DIR:?} ${WAYLAND_DISPLAY:?}/run/user/1000wayland-1For audio output, ensure that files $XDG_RUNTIME_DIR/pulse/native and $XDG_CONFIG_HOME/pulse/cookie are present. These can be created by pipewire-pulse.
Files
Create an empty directory and save the following content as Containerfile.
FROM docker.io/debian:trixie-slim
ARG MOZILLA_KEY_PATH=/etc/apt/keyrings/mozilla.ascARG MOZILLA_KEY_DIGEST=sha256:3ecc63922b7795eb23fdc449ff9396f9114cb3cf186d6f5b53ad4cc3ebfbb11f
ADD \ --checksum=${MOZILLA_KEY_DIGEST:?} \ --chmod=644 \ https://packages.mozilla.org/apt/repo-signing-key.gpg \ ${MOZILLA_KEY_PATH:?}
COPY \ <<-EOF /etc/apt/sources.list.d/mozilla.sources Types: deb URIs: http://packages.mozilla.org/apt Suites: mozilla Components: main Signed-By: ${MOZILLA_KEY_PATH:?}EOF
RUN \ --mount=type=cache,sharing=locked,target=/var/cache/apt \ --mount=type=cache,sharing=locked,target=/var/lib/apt \ set -eux; \ mv -v /etc/apt/apt.conf.d/docker-clean /tmp/; \ apt-get update; \ apt-get -y satisfy --no-install-recommends \ firefox-nightly \ libpulse0 \ ; \ mv -v /tmp/docker-clean /etc/apt/apt.conf.d/; \ :
ENTRYPOINT ["firefox-nightly"]In the same directory, save the following as compose.yaml.
---# yaml-language-server: $schema=https://raw.githubusercontent.com/compose-spec/compose-spec/refs/heads/main/schema/compose-spec.json
x-vars: environment: - &env-wayland-display null - &env-xdg-runtime-dir /tmp volumes: - &volume-user-config-mozilla source: user-config-mozilla target: /root/.config/mozilla type: volume - &bind-pulse-cookie read_only: true source: ${XDG_CONFIG_HOME:-~/.config}/pulse/cookie target: /root/.config/pulse/cookie type: bind - &bind-pulse-socket read_only: true source: ${XDG_RUNTIME_DIR:?}/pulse/native target: /tmp/pulse/native type: bind - &bind-wayland-socket read_only: true source: ${XDG_RUNTIME_DIR:?}/${WAYLAND_DISPLAY:?} target: /tmp/${WAYLAND_DISPLAY:?} type: bind
services:
firefox-nightly: build: context: . dockerfile: Containerfile environment: WAYLAND_DISPLAY: *env-wayland-display XDG_RUNTIME_DIR: *env-xdg-runtime-dir volumes: - *volume-user-config-mozilla - *bind-pulse-cookie - *bind-pulse-socket - *bind-wayland-socket
volumes: user-config-mozilla: nullPlay around with it
In the directory containing the two files, running the following command will open a browser.
podman compose run --rm firefox-nightlyWhen you open Firefox Labs, you should find a checkbox to enable JPEG XL.
Screenshot of Firefox Labs configuration page


Enable the above settings and visit JPEG XL Test Page, and you should see JPEG XL being rendered.
Since we’ve also set up the audio, you should be able to play your favorite Taylor Swift music clip with sound.
Practical example
As a practical example, I’ve created an example project for using the container to develop an Astro site.
Clone the repository and launch the containers.
git clone https://github.com/mocknen/blog-resource.gitcd blog-resource/posts/running-firefox-nightly-in-a-container/example-projectpodman compose upThe example webpage on http://node:4321/ will open in the browser inside the container. Once Astro’s development server starts up, reload the page to view it.
Astro supports hot reloading, so you can see changes in real time by editing index.astro.
You can also access it from your host environment’s browser using the following command.
(port=$(podman compose port node 4321) && xdg-open http://localhost:${port:?})After the experiment is finished, delete them as follows.
podman compose down -vWrap-up
By default, utilities run in containers cannot access the host’s display. However, as explained in this post, you can handle not only CLI tools but also GUI ones with just a little configuration.
While containers have limitations, once you create an image, it becomes easier to secure a reproducible environment isolated from the host system. This point may find value in web developments where rendering results can vary depending on browser type and version.
Compared to system containers or virtual machines, application containers are lightweight, and I like how easy it is to create them and instantly discard after use.