Being a newbie to Nix(OS), but very intrigued with the concepts, I’m trying to build examples of how my daily projects may work with Nix. However, I’m kneeling deep into the Java Ecosystem and run my business applications as Spring Boot Apps, tested with JUnit and Testcontainers. I already sacrificed purity, because I couldn’t get Gradle2nix to work on aarch64-darwin.
However I’m absolutely stuck with Testcontainers right now. I’m not working alone, so “just ditch Testcontainers” is no feasible solution to me. Also in order to move to a more nix-friendly approach, I would have to get colleagues on board, which does not look promising at this point in time.
I put my efforts into my GitHub in order to preserve my learnings from now on.
My goal
- Using Java 17
- Spring Boot
- Custom JRE (as small as possible)
- Testcontainers for integration testing with JUnit
- aarch64 Docker image being built
My setup
- MacBook Pro 16" 2021; M1 Max; 32 GB RAM
- macOS Sonoma 14.4
- nix-darwin Setup
- linux-builder enabled:
linux-builder = {
enable = true;
ephemeral = true;
maxJobs = 4;
config = {
nix.settings.sandbox = false; # cannot get it working in a pure fashion
networking = {
nameservers = [ "8.8.8.8" "1.1.1.1" ];
};
virtualisation = {
darwin-builder = {
diskSize = 40 * 1024;
memorySize = 8 * 1024;
};
docker = {
enable = true;
rootless = {
enable = true;
setSocketVariable = true;
};
};
cores = 6;
};
};
};
Building
My Flake looks like that:
{
description = "Inventory Backend Flake";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
};
version = "0.0.1-SNAPSHOT";
inventory-jre = pkgs.stdenv.mkDerivation {
name = "inventory-jre";
buildInputs = [ pkgs.openjdk17 ];
src = self;
buildPhase = ''
jlink --add-modules java.base,java.xml --output custom-jre
'';
installPhase = ''
mkdir -p $out
cp -r custom-jre/* $out/
chmod +x $out/bin/*
'';
};
application = pkgs.stdenv.mkDerivation {
# disabling sandbox
__noChroot = true;
name = "inventory-backend";
src = self;
version = version;
buildInputs = [ pkgs.openjdk17 ];
buildPhase = ''
export GRADLE_USER_HOME=$(mktemp -d)
chmod +x ./gradlew
./gradlew clean build --info
'';
installPhase = ''
mkdir -p $out
cp -r build/libs/inventory-backend-${version}.jar $out/
'';
};
dockerImage = pkgs.dockerTools.buildImage {
name = "inventory-backend";
tag = "latest";
created = builtins.substring 0 8 self.lastModifiedDate;
copyToRoot = [application inventory-jre];
config = {
Cmd = [ "${inventory-jre}/bin/java" "-jar" "${application}/inventory-${version}.jar" ];
ExposedPorts = {
"8080/tcp" = {};
};
Volumes = {
"/tmp" = {};
};
};
};
in {
devShells.default = pkgs.mkShell {
buildInputs = [ pkgs.openjdk17 ];
};
packages.default = application;
packages.dockerImage = dockerImage;
defaultPackage = self.packages.default;
}
);
}
In order to build the docker image, I’m running nix build -vvv .#packages.aarch64-linux.dockerImage --print-out-paths
, because I want to create a docker image to be run on aarch64-linux docker.
Error
Trying to build the image as described above, it actually fails, because Testcontainers wouldn’t find a valid Docker environment when building for aarch64.
Even adding a working docker environment to my linux-builder did not do the trick.
Error log:
2024-03-18T14:39:38.826Z ERROR 3789 --- [ Test worker] o.t.d.DockerClientProviderStrategy : Could not find a valid Docker environment. Please check configuration. Attempted configurations were:
As no valid configuration was found, execution cannot continue.
See https://www.testcontainers.org/on_failure.html for more details.
2024-03-18T14:39:39.831Z ERROR 3789 --- [ Test worker] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Exception during pool initialization.
java.lang.IllegalStateException: Could not find a valid Docker environment. Please see logs and check configuration
at org.testcontainers.dockerclient.DockerClientProviderStrategy.lambda$getFirstValidStrategy$7(DockerClientProviderStrategy.java:256)
at java.base/java.util.Optional.orElseThrow(Optional.java:403)
at org.testcontainers.dockerclient.DockerClientProviderStrategy.getFirstValidStrategy(DockerClientProviderStrategy.java:247)
at org.testcontainers.DockerClientFactory.getOrInitializeStrategy(DockerClientFactory.java:150)
at org.testcontainers.DockerClientFactory.client(DockerClientFactory.java:186)
at org.testcontainers.DockerClientFactory$1.getDockerClient(DockerClientFactory.java:104)
at com.github.dockerjava.api.DockerClientDelegate.authConfig(DockerClientDelegate.java:108)
at org.testcontainers.containers.GenericContainer.start(GenericContainer.java:321)
at org.testcontainers.jdbc.ContainerDatabaseDriver.connect(ContainerDatabaseDriver.java:134)
at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138)
at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:359)
at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:201)
at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:470)
at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:561)
at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:100)
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112)
What I know
- Testcontainers spins up docker containers when running the JUnit tests of my project.
- skipping tests is no viable option
- ditching Testcontainers is not a viable option at the moment
- Testcontainers looks for
DOCKER_HOST
My linux-builder has a valid DOCKER_HOST
and a working docker setup:
[builder@nixos:~]$ echo $DOCKER_HOST
unix:///run/user/1000/docker.sock
However in the log it says WARN 3789 --- [ Test worker] o.t.d.DockerClientProviderStrategy : DOCKER_HOST unix:///var/run/docker.sock is not listening
.
So I suppose, it uses a separate environment.
How do I set up a docker build dependency in Nix(OS) correctly? Where can I go deeper to get an understanding of what to do?