gmile

gmile

Smallest docker container with elixir app

After docker released native support for multi-stage builds, I decided to make my own small docker container with app.

Below container is based on the one from @bitwalker. It is the smallest one I could come up with in one sitting, given quite limited knowledge of docker & distillery releases:

# Dockerfile
FROM bitwalker/alpine-elixir:1.4.4 as builder

ADD . /app

WORKDIR /app

ENV MIX_ENV=prod

RUN mix do deps.get, deps.compile, release

FROM alpine:3.6

RUN apk add --no-cache \
      ca-certificates \
      openssl-dev \
      ncurses-dev \
      unixodbc-dev \
      zlib-dev

WORKDIR /app

COPY --from=builder /app/_build/prod/rel/ritm/releases/0.0.1/ritm.tar.gz /app

ENV PORT=4000

RUN tar -xzf ritm.tar.gz; rm ritm.tar.gz

CMD ["bin/ritm", "foreground"]

It’s around 64Mb when pushed to docker hub. That’s a lot, although I don’t expect this to grow significantly (more app code & deps shouldn’t contribute that much).

I am aware guys from @Nerves-Core-Team build super-small linux images with the app installed, but I am not sure what could be derived from their work in order to build my own small container with docker.

How can I reduce the size of container even further?

Most Liked Responses

gmile

gmile

In case anyone is interested, this is the container I’m building right now. It uses:

  • alpine:3.6 as a builder/runner image,
  • Erlang 20.1.1
  • Elixir 1.5.2

I like to build my own image instead of a pre-backed image in this case. I like to install elixir/erlang, as well as other packages, instead of compiling them. By looking at base image, I know that there’s no magic going on here.

It’s size, including my apps compiled code and dependencies is 55.5 Mb.

FROM alpine:3.6 as builder

RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/community/" >> /etc/apk/repositories

RUN apk add --update \
      git \
      erlang=20.1.1-r0 \
      elixir=1.5.2-r0 \
      erlang-crypto \
      erlang-parsetools \
      erlang-syntax-tools \
      erlang-runtime-tools

ADD . /app

WORKDIR /app

ENV MIX_ENV=prod

RUN mix do local.hex --force, \
           local.rebar --force, \
           deps.get, \
           deps.compile, \
           release

FROM alpine:3.6

RUN apk add --no-cache \
      ncurses-libs \
      zlib \
      ca-certificates \
      openssl \
      bash

WORKDIR /app

COPY --from=builder /app/_build/prod/rel/my_app/releases/0.0.1/my_app.tar.gz /app

RUN tar -xzf my_app.tar.gz; rm my_app.tar.gz

I’m relying on distillery for building releases. After the container is built, the app must be started by running bin/my_app foreground in the container:

docker run my_app_image bin/my_app foreground
gmile

gmile

So, the Dockerfile I’m using has changed significantly. I’m now using two of them:

  • a base one, which is updated much less frequently,
  • and the apps one, which is ran every time I need to pack the app into a container.

This is how they look like:

Base:

# edenlabllc/elixir:1.5.2

FROM alpine:edge

ENV REFRESHED_AT=2017-11-23

RUN apk add --update \
  erlang=20.1.7-r0 \
  elixir=1.5.2-r0 \
  erlang-crypto \
  erlang-parsetools \
  erlang-syntax-tools \
  erlang-runtime-tools \
  git \
  make

Everyday use:

FROM edenlabllc/elixir:1.5.2 as builder

ARG APP_NAME
ARG APP_VERSION

ADD . /app

WORKDIR /app

ENV MIX_ENV=prod

RUN mix do \
      local.hex --force, \
      local.rebar --force, \
      deps.get, \
      deps.compile, \
      release

FROM alpine:edge

ARG APP_NAME
ARG APP_VERSION

RUN apk add --no-cache \
      ncurses-libs \
      zlib \
      ca-certificates \
      openssl \
      bash

WORKDIR /app

COPY --from=builder /app/_build/prod/rel/${APP_NAME}/releases/${APP_VERSION}/${APP_NAME}.tar.gz /app

RUN tar -xzf ${APP_NAME}.tar.gz; rm ${APP_NAME}.tar.gz

ENV REPLACE_OS_VARS=true \
    APP=${APP_NAME}

CMD ./bin/${APP} foreground

In order to build this, I run:

docker build --tag "my_account/my_app:1.2.3" \
             --file Dockerfile \
             --build-arg APP_VERSION=1.2.3. \
             --build-arg APP_NAME=my_app .

I could merge both Dockerfiles, but it just didn’t make sense to re-install the compile-time dependencies every time I needed to build the app. After all, elixir and erlang release versions much less frequently.

melpon

melpon

I think openssl package is not needed if Erlang is built with ./configure --disable-dynamic-ssl-lib ....

Where Next?

Popular in Questions Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lists...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

We're in Beta

About us Mission Statement