Error loading module poolboy inside a docker container

Well, I’m dockerizing a Phoenix app and I’m getting the following error when running mix phx.server:

[error] beam/beam_load.c(1287): Error loading module poolboy:
  mandatory chunk of type 'Atom' not found

That causes this error when starting the application:

 ** (UndefinedFunctionError) function :poolboy.child_spec/3 is undefined (module :poolboy is not available)

Anyone had the same issue someday? Any clue?

1 Like

This probably means you’re trying to run code compiled on OTP 20 on an earlier release.

4 Likes

Which probably means your Erlang in docker differs from your Erlang outside of docker, and you have compiled the app on the host system. Clean the build and try again building it from within docker.

2 Likes

Why build in docker? This will bloat the image with useless cruft not needed during runtime, just blowing the containersize and with that transfer time, so time until a new one is started increases as well…

Build the release with ERTS embedded on the hostsystem and transfer it into a naked base image of your host systems distribution, perhaps even a minimized one.

Also install libraries as in the container, not more, not less. Remember to clean the package managers cache in the same RUN, eg.:

BASE debian:latest

RUN apt update \
  && apt --yes upgrade \
  && apt --yes install postgresql-client \
  && apt --yes autoremove \
  && apt --yes autoclean

ADD phoenix_release /usr/local/bin/phoenix_release

ENTRYPOINT ["/usr/local/bin/phoenix_release"]
CMD ["start"]

This is a draft from the top of my head, can’t guarantee for it to actually work :wink: But thats roughly the concept.

To keep images small, one needs to delete files in the same step as they were created, or they need they stick in your image in an earlier overlay, even when hidden, they occupy space.

1 Like

I’m using https://hex.pm/packages/mix_docker with custom Dockerfile.build and Dockerfile.release files.

Using a docker image to build the release means it’s possible to create the release image in any OS and still use Alpine Linux as the image base for minimum size. In my case the resulting image with embedded Erlang 20 (erts) and the application is under 75MB.

The build image itself has over 300MB. It’s the same Alpine base system to avoid any issues with dynamically linked libraries plus all the required compile time only dependencies.

After the initial effort of creating the two Dockerfiles, running mix docker.shipit will generate the release, create the new image and finally push it to the Docker registry.

2 Likes

Of course this is a suitable way to go as well, building and deploying on to different docker images. I’d actually do this for a couple of go-projects at work, but it was quite a hazzle to set up the system and pull out the binary after compilation, therefore I didn’t even mentioned that.

If I had known that there is a package on hex which helps you with that boilerplate stuff, I’d suggested that one.

Thanks everyone for the insights!

@hubertlepicki you and @michalmuskala were totally right! Problem fixed. :wink:

@NobbZ, that’s the direction I intend to go in the future. And @pma, thanks for the mix_docker tip, I’ll totally try it soon!

This is no longer true. Docker has now stages, so you can have a stage for build, then clean up, and then build final stage of production image. This results in production images not including any of the build phases. It’s actually pretty neat and convenient. I build Elixir app this way, and my final stage has no node.js or Elixir or Erlang nor any of dependent libraries and it results in pretty lean image. https://docs.docker.com/engine/userguide/eng-image/multistage-build/

2 Likes

Thank you for providing this piece of information. I will take a closer look today, this might simplify things at work a lot :wink:

But why don’t they mention it in the “best practices" as a size reducing measure?

1 Like

I don’t know, but it is really new. It is only available on the most recent docker release.

1 Like