Astolfo

Astolfo

Use alpine for phx.gen.release --docker

When generating a release with phx.gen.release --docker a debian version is used by default to avoid “DNS issues” on Alpine.

It seems like the mentioned issues have been resolved in Alpine 3.18:

Because Alpine images are much smaller, and as far as I’m aware have fewer vulnerabilities.
I suggest either switching to alpine by default or adding an option to generate Dockerfile that uses alpine as a base image (like phx.gen.release --docker --alpine).

If the community agrees, I can work on a pull request.

Thank you,
Anna

Most Liked

LostKobrakai

LostKobrakai

While DNS might have been a truely blocking issue I still wouldn’t call alpine a great option for a default. People will eventually add a NIF based dependency where runing musl has a high likelyhood of breaking things.

Personally I also don’t understand the obsession with image size given docker caching that stuff anyways.

mwilsoncoding

mwilsoncoding

I originally agreed that it would be nice to have options in the generator to customize the variant. My work asks me to ship alpine images where possible, so it’s a frequent concern.

For example, the following could explicitly generate the current state/default:

mix phx.gen.release \
  --docker \
  --elixir-version='1.18.3' \
  --otp-version='27.3' \
  --variant=debian \
  --variant-version='bullseye-20250317-slim'

--variant could specify the OS variant to base your build/run on and --variant-version its version.

Then again, it seemed easy enough to tweak the default Dockerfile to support all three OS options. But with this solution, I’m back to not needing new options in the generator.

Only 2 meaningful changes. First toward the top:

...

ARG VARIANT=debian
ARG VARIANT_VERSION=bullseye-20250317-slim

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

FROM ${BUILDER_IMAGE} AS builder

ARG VARIANT

# install build dependencies
RUN case "${VARIANT}" in \
    "debian" | "ubuntu") \
        apt-get update -y && apt-get install -y build-essential git \
        && apt-get clean && rm -f /var/lib/apt/lists/*_* \
    ;; \
    "alpine") \
        apk add --no-cache build-base git \
    ;; \
    *) \
        echo "Unsupported variant: ${VARIANT}" && exit 1 \
    ;; \
    esac

...

Here, we:

  • introduce the VARIANT build arg
  • switch DEBIAN_VERSION to VARIANT_VERSION
  • switch debian to ${VARIANT}
  • RUN a case statement to toggle OS package manager setup commands based on the value of VARIANT

Then toward the end of the file:

...

FROM ${RUNNER_IMAGE}

ARG VARIANT

RUN case "${VARIANT}" in \
    "debian" | "ubuntu") \
        apt-get update -y && apt-get install -y libstdc++6 openssl libncurses5 locales ca-certificates \
        && apt-get clean && rm -f /var/lib/apt/lists/*_* \
    ;; \
    "alpine") \
        apk add --no-cache libstdc++ openssl ncurses-dev musl-locales musl-locales-lang ca-certificates \
    ;; \
    *) \
        echo "Unsupported variant: ${VARIANT}" && exit 1 \
    ;; \
    esac

# Set the locale
ENV MUSL_LOCPATH="/usr/share/i18n/locales/musl"

RUN if [ "${VARIANT}" = "debian" ] || [ "${VARIANT}" = "ubuntu" ]; then \
        unset MUSL_LOCPATH && sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen ; \
    fi

ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8

...

Here, we:

  • use the same case strategy to set up OS deps on the runner image
  • finish out alpine locale setup by assuming an alpine build and setting MUSL_LOCPATH
  • if not alpine, unset MUSL_LOCPATH and perform remaining locale setup command for ubuntu/debian

I’ve adapted the above solution from this post for setting up the locale in alpine. Tho this post directed me to musl-locales-lang over lang.

To get the various image types:

docker build \
  -t my-ubuntu-app \
  --build-arg VARIANT=ubuntu \
  --build-arg VARIANT_VERSION=focal-20241011 \
  .
docker build \
  -t my-debian-app \
  --build-arg VARIANT=debian \
  --build-arg VARIANT_VERSION=buster-20240612 \
  .
docker build \
  -t my-alpine-app \
  --build-arg VARIANT=alpine \
  --build-arg VARIANT_VERSION=3.21.3 \
  .

Available options for VARIANT and VARIANT_VERSION were found by browsing the available tags on Dockerhub.

Your mileage may vary, but this at least works for me on a freshly generated LiveView app using Elixir 1.18.3 on OTP 27.3.

Astolfo

Astolfo

There are many reasons to prefer smaller images:

  • Faster build times
  • Deployments in constrained environments
  • Smaller surface area for vulnerabilities, for example debian:12-slim vs alpine:latest (base images). This problem with debian docker images and phoenix was also mentioned here

Are you sure there are that many popular libraries that don’t work with musl? Could you provide some examples?

Where Next?

Popular in Proposals: Ideas Top

sbennett33
When building a component library, it is often useful to give users the ability to customize the underlying element or component to use. ...
New
woylie
We are seeing a lot of warning logs like this: navigate event to "https://someurl" failed because you are redirecting across live_sessio...
New
azyzz228
The slow network is known to be an Achilles heel of LiveView’s architecture. Recently, I was working on creating a fast rendering map wi...
New
andreamancuso
Hey folks, This might sound niche, but I think it’s worth bringing up - especially given Phoenix’s reputation for being lightweight, por...
New
marcandre
I notice that most events have bindings (e.g. phx-keyup) but not the input event. The input event is the preferred way to interact with ...
New
engineeringdept
In 2026 double submit/session tokens are no longer necessary to prevent against CSRF attacks. Instead, we can use the Sec-Fetch-Site head...
New
sevensidedmarble
Hello all, Apologies if this has been proposed before I guess, but I have a very simple one: With the increasing importance of LV, I th...
New
BartOtten
I’d like to propose that we refrain from using the term "DeadView" as the opposite of “LiveView” and instead choose an alternative. A new...
New
spicychickensauce
I’ve previously explored what is possible today with hacks to implement view transitions in our apps: I have since created a fork to im...
New
Flo0807
Hello everyone! Phoenix LiveView v0.18 introduced the special attributes :let, :for and :if. In addition to the :if special attribute, I...
New

Other popular topics Top

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
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
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
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
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
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

We're in Beta

About us Mission Statement