# almond gives Jupyter a Scala kernel and a documented Docker build

> almond is a Scala kernel for Jupyter, distributed on Maven Central under sh.almond and used to build notebooks against Spark. The honest reading of this repository is that the Dockerfile and the Docker Hub launcher images are the supported path, while the website documentation lags behind the sources.

**almond-sh/almond** — A Scala kernel for Jupyter

- Repository: https://github.com/almond-sh/almond
- Website: https://almond.sh
- Stars: 1,630 · Forks: 245
- Language: Scala
- License: BSD-3-Clause
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/almond-sh-almond

## Why a Scala kernel needs its own distribution story

Jupyter does not run Scala. It launches kernels through a kernel spec, and for Scala that spec has to resolve an artifact, a JVM and a Scala version that agree with each other. almond exists to make that agreement repeatable rather than something each user assembles by hand, which is why the project ships as an ordinary Maven Central artifact rather than only as source. The README carries a Maven Central badge for `sh.almond:kernel_2.12`, which tells you the group id and that at least the Scala 2.12 line is published there.

The topics attached to the repository mention jupyter, jupyter-kernels, jupyter-notebook, repl, scala, spark and spark-sql. That combination is the honest positioning: this is a REPL turned into a kernel, intended for notebooks that talk to Spark rather than for a general Scala IDE replacement. Anyone arriving from a Python or R kernel will find the model familiar, but the interesting case is the one where the notebook process itself is expected to hold a live Spark session.

## The Dockerfile is the install instructions

The README is short and delegates to the project website, but the repository's Dockerfile carries the substantive detail, including its own usage comment. It supports building images that carry kernels for multiple Scala versions at once, driven by two build arguments:

```dockerfile
# docker build --build-arg ALMOND_VERSION=0.13.11 --build-arg SCALA_VERSIONS="2.12.19 2.13.11" .
```

Note that the version in the Dockerfile's own comment, 0.13.11, is older than the recent releases listed for the repository, which include 0.15.0 from 2026-09-28 and a nightly launcher build from 2026-10-08. The comment is an illustration of the syntax, not a recommendation of that pin.

A third argument controls how the kernel gets into the image. Setting `LOCAL_IVY=yes` copies the contents of an `ivy-local/` directory into `.ivy2/local/`, which the Dockerfile describes as the way to create an image with a locally built almond that is not on Maven Central yet. The default is `no`, and the file implements that choice as separate build stages (`local_ivy_yes` and `local_ivy_no`) that are joined through `FROM local_ivy_${LOCAL_IVY}`, so the branching happens at image assembly rather than at runtime.

## Coursier and OpenJDK 17 are baked into the base image

The base image is `quay.io/jupyter/base-notebook`, which already supplies the Jupyter side, and almond adds the JVM toolchain on top of it:

```dockerfile
RUN apt-get -y update && \
    apt-get install --no-install-recommends -y \
      curl \
      openjdk-17-jre-headless \
      ca-certificates-java && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*
```

Only a headless JRE is installed, which is consistent with a container that runs code rather than one that compiles it interactively. The Dockerfile then installs two coursier launchers, a native one as `cs` and a JVM one as `coursier`, pinned to `COURSIER_VERSION=2.1.26`, and selects between architectures with a shell case: on `aarch64` it takes the `container` variant of the native launcher, everywhere else the generic Linux one.

That aarch64 branch is not cosmetic. The comment above it explains that GraalVM native images can crash when reading cgroup files inside containers, so on ARM64 the `container` build is used with GraalVM native-image support disabled, citing a coursier pull request. Anyone rebuilding this image on their own base needs to preserve that split, and anyone debugging coursier failures on ARM64 will find it is a known constraint rather than their own mistake. The Dockerfile switches back to the non-root `$NB_UID` user before the almond stage, matching the base notebook image's convention.

## Mill, not sbt, and a repository shaped around modules

The build tool is Mill, not sbt or Gradle. The repository root contains a `mill` script, a `mill.bat` for Windows, `build.mill`, a `mill-build/` directory and a `.mill-jvm-opts` file, alongside `modules/`, `app/`, `scripts/` and `docs/`. This matters for contributors: a Scala project built with Mill does not load in an sbt shell, and there is no `build.sbt` to open. The presence of `mill.bat` also tells you the build is expected to work on Windows, which is a reasonable expectation for a kernel meant to run wherever a notebook does.

The `examples/` directory is where the project shows what the kernel is supposed to do. It holds `colors.ipynb`, `displays.ipynb`, `plotly-scala.ipynb` and `test.ipynb`, along with a `jupyterlab-overrides.json`, a `pyproject.toml`, a `uv.lock` and a `.python-version`. Two of those entries deserve comment. A JupyterLab overrides file implies the kernel is expected to work inside JupyterLab, not only classic Notebook. A `pyproject.toml` with a `uv.lock` alongside Python example notebooks implies the example environment pins Python tooling separately from the JVM side, which is a hint that some example notebooks lean on Python interop rather than being pure Scala. The `examples/acp-agents/` directory suggests work on agent-facing notebook use, but the repository does not document what it contains in the README, so treat it as a direction rather than a feature.

## Where almond is the wrong choice

The documentation gap is the first limitation, and the README is unusually candid about it. It points developer documentation at `docs/pages/dev-from-sources.md` in the sources, and says the corresponding page on the website will be updated soon, adding that it is currently out of date there. A project whose main documentation site is self-declared stale is a poor fit for a team that needs to answer operational questions quickly.

Second, there is no plain-language install path in the README. No pip line, no conda line, no `jupyter kernelspec install` invocation appears there. A user who lands on the repository without reading the Dockerfile or the website has no documented way to install the kernel at all. That is not a fatal problem, since the Docker route is real and the Maven artifact exists, but it does mean the first hour of adoption depends on the launcher images or on reading build scripts.

Third, the artifact badge names only the Scala 2.12 variant. The Dockerfile's SCALA_VERSIONS argument shows that several Scala versions are supported in one image, but nothing in the README states which Scala lines are supported today or how the artifact suffix changes for them, so a team on a specific Scala version has to confirm coverage before committing.

## Licence and release cadence

almond is licensed under 3-Clause BSD, with the LICENSE file at the repository root. The README's Code of Conduct section adds a constraint that is easy to miss: all code or documentation provided must be licensed under the same 3-Clause BSD licence that almond itself uses, so a contribution carrying incompatible terms cannot be merged. Discussion is expected to follow the Scala Code of Conduct on GitHub and on the Gitter channel.

On cadence, the recent releases show an interesting split. Version 0.15.0 shipped on 2026-09-28, but the entry before it, 0.14.5, dates from 2026-02-03, a gap of about eight months between those two tags. Alongside them the project publishes a nightly track labelled Nightly launchers, the most recent on 2026-10-08. The repository is not archived and the last push was on 2026-10-16, so work is landing; the practical reading is that stable tags arrive infrequently while the project also publishes nightly launcher builds. Teams that want reproducible notebooks should pin a released version rather than tracking nightly.

## Conclusion

Adopt almond when your notebook work is Scala, particularly when the notebook has to share a JVM with Spark, and install it by building the repository Dockerfile with ALMOND_VERSION and SCALA_VERSIONS set. Do not expect the website at almond.sh to answer operational questions: the README itself says the developer documentation on the site is currently out of date, so treat docs/pages in the sources as authoritative. Before pinning a version, check that the Scala version you need is covered by the launcher image or your own multi-version build, since the README badge only shows the sh.almond:kernel_2.12 artifact.

## FAQ

### How do I install the almond Scala kernel for Jupyter?

The README does not give a pip or conda install line. The documented route is the project website at almond.sh, and the repository's own Dockerfile shows the container build taking ALMOND_VERSION and SCALA_VERSIONS as build arguments. The kernel is also published to Maven Central under sh.almond:kernel_2.12.

### Can one almond Docker image carry several Scala versions?

Yes. The Dockerfile is described as supporting images with kernels for multiple Scala versions, and the SCALA_VERSIONS build argument is given as a space-separated list, for example 2.12.19 2.13.11. The same file also accepts ALMOND_VERSION and an optional LOCAL_IVY flag.

### Why does the almond Dockerfile treat aarch64 differently?

On ARM64 it uses the container variant of the coursier native launcher built with GraalVM native-image support disabled, because the Dockerfile states that GraalVM native images can crash when reading cgroup files inside containers. Other architectures use the generic Linux native launcher.

### Which build tool does almond use?

The repository root carries a mill script, a mill.bat for Windows, build.mill, a mill-build directory and a .mill-jvm-opts file, so the build is Mill based. There is no build.sbt in the top-level entries.

## Sources

- [almond-sh/almond on GitHub](https://github.com/almond-sh/almond)
- [License: BSD-3-Clause](https://github.com/almond-sh/almond/blob/main/LICENSE)
- [Project website](https://almond.sh)
- [README](https://github.com/almond-sh/almond/blob/main/README.md)
- [Releases](https://github.com/almond-sh/almond/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/almond-sh-almond
