I’m runnning NixOS in a VM on an M1 mac. My application runs via docker compose.
One of the docker services is a mhart/alpine-node:14. When I run docker compose natively on macOS for my application, everything runs fine.
If I do the same in the nixOS VM, the docker mhart/alpine-node:14 image doesn’t run on the right platform and I get error:
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
exec /usr/bin/npm: exec format error
I can’t figure out why docker behaves differently wrt to platforms when run in Nix.
Firstly just wanna say thanks for replying everyone.
The image is x86 but I’ve run the same stack on my Ubuntu desktop and I didn’t have this problem. Docker-compose I assume passes platform target automatically, but somehow that isn’t happening in nix ? Not sure.
So my nixos isn’t capable of building for other platforms. I’m not sure how exactly to track down what’s missing but I am following this post to see if I can get a clue for now…
I’m assuming buildx is getting it’s platform emulation via binfmt. But even after adding boot.binfmt.emulatedSystems= ["i386-linux" "x86_64-linux" ];
to my config, I still don’t get those platforms available to buildx builder.
Docker Desktop* provides binfmt_misc multi-architecture support, which means you can run
containers for different Linux architectures such as arm , mips , ppc64le , and even s390x .
This does not require any special configuration in the container itself as it uses qemu-static from the Docker for Mac VM . Because of this, you can run an ARM
container, like the arm32v7 or ppc64le variants of the busybox image.
So the entire problem was that my system didn’t have the emulators for other platforms somehow.
In all the docker buildx tutorials you see out there, people had cross platform emulation out of the box but my system only showed linux/arm64 when I ran docker buildx inspect .
Until I found this Repo. I ran docker run --privileged --rm tonistiigi/binfmt --install all just to make sure and finally my platform option in docker-compose or docker build worked.
So if anyone is stuck with the same problem, check what you get under the platforms column when you run docker buildx ls. If there’s only your own platform, you need to add the qemu emulators manually or via this docker command from the above linked repo. Thanks for your help @Atemu
Hello,
I have recently started tinkering with NixOS and I faced the same issue. For me the problem was that the qemu-{platform} given by NixOS are dynamically linked which Docker does not like.
As a workaround (still ugly but at least declarative), I stole the qemu-user-static binaries from the debian repos:
In my case I am doing linux/arm64 emulation from an amd64 laptop, but it would be possible to do the other way around by grabbing the qemu-user-static binaries in the debian arm repos instead and setting up the binfmt registration for amd64 by changing the interpreter path, magic and mask.
Additionally, as you can see in the above config, I had to add the fixBinary flag and disable the wrapInterpreterInShell. By the way I am curious to know why NixOS default behavior is to wrap the interpreter in a shell script, what does it accomplish?
Hope this helps. And if someone thinks of a way to improve on that solution, please share it. Maybe some version of qemu static binaries should be present in nixpkgs to simplify this use case, what do you think?
The option to use statically-linked QEMU is being worked on. That seems to have stalled a bit due to merge conflicts but #300070 rebased the changes and is currently active.
I think the wrapper handles the binfmt_miscP flag (see here for more details, ctrl-f for preserve-argv), and prevents LD_* environment variables intended to be passed to the emulated binary from affecting QEMU itself. Newer versions of QEMU handle the P flag natively (see this commit) and LD_* is irrelevant to statically-linked executables anyway, so the PR I linked to above also disables the wrapper when you are using qemu-user-static.
boot.binfmt = {
emulatedSystems = [ "aarch64-linux" ];
preferStaticEmulators = true; # Make it work with Docker
};
Seem to be enough now.
I was banging my head against why i was getting
docker run --platform linux/arm64 --rm my-app:latest
exec /nix/store/8s9a7drrh6hwzmivwmbdpjr146awk7l5-my-app-aarch64-unknown-linux-gnu/bin/my-app: no such file or directory