Release docker & build, NIF for wx not found

After generating a Dockerfile with mix release, and build it with docker build, I am unable to start elixir in the container:

 
=INFO REPORT==== 15-Dec-2023::08:20:17.080211 ===
    application: kernel
    exited: {{shutdown,
                 {failed_to_start_child,on_load,
                     {on_load_function_failed,gl,
                         {error,
                             {load_failed,
                                 "Failed to load NIF library: '/app/lib/wx-2.3.1/priv/erl_gl.so: cannot open shared object file: No such file or directory'"}}}}},
             {kernel,start,[normal,[]]}}
    type: permanent

I am using the generated Dockerfile as is with the following versions:

ARG ELIXIR_VERSION=1.15.7
ARG OTP_VERSION=26.1.2
ARG DEBIAN_VERSION=bullseye-20231009-slim

ARG BUILDER_IMAGE="hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}"
ARG RUNNER_IMAGE="debian:${DEBIAN_VERSION}"

What is missing here? build and execution on Apple Silicon

1 Like

So in my case, the issue was a dependency which uses

extra_applications: [:logger, :runtime_tools, :wx, :observer]

:wx and :observer removed and the container runs as expected. Unfortunately mix deps.tree does not show such dependencies. To find the one I searched for :wx and :observer in the deps directory. Be aware that in VSCode this folder might be excluded from global search, so use grep or similar form the command line.

For future reference, these applications are part of OTP and are bundled together with erlang runtime, if declared of course.

In earlier versions of elixir, all packages would be usually bundled without having to specifically add them, this changed in version 1.15 (or maybe earlier), now you have to specifically declare them if you want to bundle them with your release.

On the dev side, the libraries will work without adding extra_applications as long as your local erlang installation compiled all of them.

2 Likes

With specifically add them you mean also add them to my-app extra_applications?

The behavior you are encountering above might be subject to a potential bug, if either your app or a library has extra_applications defined, the OTP libraries should be bundled with release. I misread before, thinking that you are using those libraries from your own application.

A simple example would be the fact that phoenix uses crypto, which is an OTP library, and you don’t have to declare it in your actual application.

1 Like

Hey,
I’ve been struggling with the exact same issue today, but the solution suggested doesn’t work and I don’t understand why. I can run the application locally just fine, but when built as a production build using mix release it blows up with the following traceback.

=CRASH REPORT==== 23-Aug-2024::18:59:44.248395 ===
  crasher:
    initial call: kernel:init/1
    pid: <0.5565.0>
    registered_name: []
    exception exit: {on_load_function_failed,gl,
                        {error,
                            {load_failed,
                                "Failed to load NIF library: 'Error loading shared library /opt/app/lib/wx-2.4.2/priv/e
rl_gl.so: No such file or directory'"}}}
      in function  init:run_on_load_handlers/0
      in call from kernel:init/1 (kernel.erl, line 228)
    ancestors: [kernel_sup,<0.5558.0>]
    message_queue_len: 0
    messages: []
    links: [<0.5560.0>]
    dictionary: []
    trap_exit: false
    status: running
    heap_size: 376
    stack_size: 29
    reductions: 96
  neighbours:

=SUPERVISOR REPORT==== 23-Aug-2024::18:59:44.248582 ===
    supervisor: {local,kernel_sup}
    errorContext: start_error
    reason: {on_load_function_failed,gl,
                {error,
                    {load_failed,
                        "Failed to load NIF library: 'Error loading shared library /opt/app/lib/wx-2.4.2/priv/erl_gl.so: No such file or directory'"}}}
    offender: [{pid,undefined},
               {id,on_load},
               {mfargs,{proc_lib,start_link,[kernel,init,[on_load]]}},
               {restart_type,transient},
               {significant,false},
               {shutdown,2000},
               {child_type,worker}]

In my mix.exs file I have the following, so as far as I can tell it shouldn’t try to load wx when running the build.

  def application do
    [
      mod: {Pipelines.Application, []},
      extra_applications: extra_applications(Mix.env())
    ]
  end

  defp extra_applications(:dev), do: [:logger, :observer, :wx, :runtime_tools, :os_mon]
  defp extra_applications(_), do: [:logger, :runtime_tools, :os_mon]

The application is running in a docker image using:

Docker Image:

  • elixir:1.17-alpine
    Erlang/OTP:
  • Erlang/OTP 27 [erts-15.0.1] [source] [64-bit] [smp:4:1] [ds:4:1:10] [async-threads:1] [jit:ns]
    IEx 1.17.2 (compiled with Erlang/OTP 27)

Does anybody know what could be the reason?
Cheers!

Are you setting MIX_ENV anywhere? Does the docker container have erlang built with wx enabled?

I’m using the official elixir:1.17-alpine image as my build image. I don’t know if erlang was built with wx enabled or not.

I’m also setting MIX_ENV inside my Dockerfile in the build step.
Here’s the Dockerfile for referenece.
I tried adding the erlang-wx apk to test if that would solve anything, but it had no effect.

#
# Build staging image
#
FROM    elixir:1.17-alpine AS staging

ARG     MIX_ENV=${MIX_ENV:-prod}

RUN     apk add --no-cache build-base erlang-wx git openssh-client yarn

WORKDIR /staging
COPY    mix.exs mix.lock .formatter.exs ./

# Fix for making picosat_elixir compile on alpine
RUN     mkdir -p /usr/include/sys \
          && ln -s /usr/include/unistd.h /usr/include/sys/unistd.h

RUN     mix local.hex --force \
          && mix local.rebar --force \
          && mix deps.get --only ${MIX_ENV}

COPY    rel rel
COPY    lib lib
COPY    priv priv
COPY    config config
COPY    assets assets

RUN     yarn --cwd assets install
RUN     mix phx_fontawesome.generate
RUN     mix do assets.deploy, release

#
# Build runtime image
#
FROM    elixir:1.17-alpine as runtime
ENV     HOME=/opt/app
RUN     apk --no-cache update \
          && apk --no-cache upgrade

RUN     adduser -D appuser -h ${HOME}

WORKDIR ${HOME}

COPY    --from=staging --chown=appuser /staging/_build/prod/rel/server ./
RUN     chgrp -R 0 ${HOME} && chmod -R ug+rwx ${HOME}

USER    appuser
CMD     ["bin/server", "start"]

Your mix.exs doesn’t include :wx when MIX_ENV is not dev, with this line: defp extra_applications(_), do: [:logger, :runtime_tools, :os_mon]. If you want your dev and other environments to use the same set of extra_applications, you’d want to remove that clause and change the other clause to accept any argument. I’m not sure if the erlang-wx package for apk would be needed.

Yeah, that’s the ting… I really don’t want :wx to start in production builds. That’s what so weird about this error. I was under the impression that since I don’t include it in extra_applications, it shouldn’t be loaded, but somehow it blows up anyway.

Things I have tried:

  • Include :wx in extra_applications in a production build
  • Other docker images as base image, including 1.17-otp-26, 1.16 and 1.15 images with various OTP versions.

But all of them crash and burn with the same error.

I haven’t tested yet, but I suspect I’ve found the offender. I have a dependency which is another mix project which loads :wx and :observer as part of their extra_applications. And I forgot to add runtime: false to the dependency. Running new build with fingers crossed now!

I’m sorry, I misunderstood your question. Is it possible that one of your dependencies relies on :wx?

EDIT:

I missed your last comment. If one of your dependencies is relying on :wx and :observer, that’s probably your issue. Hopefully your new build works :slight_smile:

Yes, that was it! I’ve spent all day researching this :sweat_smile:
Thank you very much!!
:duck::duck: :smiley:

1 Like

Always happy to be someone’s rubber duck. :slight_smile:

1 Like