Fly.io compile time and runtime env values mismatch

Hello, i have introduced Application.compile_env in my code and now fly.io deployment does not work any more, it fails, because compile time env does not match run time env.

I have set up all of them in fly io secrets and it shows properly at run time, but compile time they are empty. I have red Build Secrets · Fly Docs like 20 times, i just cant grasp what i don’t understand. I dont understand what is Dockerfile.builder, i only have one Dockerfile.

Optimally i would like that the same secrets defined in fly.io can be assigned to env at compile time as well, and i should not pass them from github action or smth.

My current Dockerfile. It is slightly changed for beacon cms to work.

# Find eligible builder and runner images on Docker Hub. We use Ubuntu/Debian
# instead of Alpine to avoid DNS resolution issues in production.
#
# https://hub.docker.com/r/hexpm/elixir/tags?page=1&name=ubuntu
# https://hub.docker.com/_/ubuntu?tab=tags
#
# This file is based on these images:
#
#   - https://hub.docker.com/r/hexpm/elixir/tags - for the build image
#   - https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye-20241202-slim - for the release image
#   - https://pkgs.org/ - resource for finding needed packages
#   - Ex: hexpm/elixir:1.18.0-erlang-27.1.2-debian-bullseye-20241202-slim
#
ARG ELIXIR_VERSION=1.18.0
ARG OTP_VERSION=27.1.2
ARG DEBIAN_VERSION=bullseye-20241202-slim

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

FROM ${BUILDER_IMAGE} as builder

# install build dependencies
RUN apt-get update -y && apt-get install -y build-essential git npm \
    && apt-get clean && rm -f /var/lib/apt/lists/*_*

# prepare build dir
WORKDIR /app

# install hex + rebar
RUN mix local.hex --force && \
    mix local.rebar --force

# set build ENV
ENV MIX_ENV="prod"

# install mix dependencies
COPY mix.exs mix.lock ./
RUN mix deps.get --only $MIX_ENV
RUN mkdir config

# copy compile-time config files before we compile dependencies
# to ensure any relevant config change will trigger the dependencies
# to be re-compiled.
COPY config/config.exs config/${MIX_ENV}.exs config/

RUN mix deps.compile

COPY priv priv

COPY lib lib

COPY assets assets

# compile assets
RUN npm install --prefix assets
RUN mix assets.deploy

# Compile the release
RUN mix compile


# Changes to config/runtime.exs don't require recompiling the code
COPY config/runtime.exs config/

RUN mix assets.setup

COPY rel rel
RUN mix release

# start a new build stage so that the final image will only contain
# the compiled release and other runtime necessities
FROM ${RUNNER_IMAGE}

RUN apt-get update -y && \
  apt-get install -y libstdc++6 openssl libncurses5 locales ca-certificates \
  && apt-get clean && rm -f /var/lib/apt/lists/*_*

# Set the locale
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen

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

WORKDIR "/app"
RUN chown nobody /app

# set runner ENV
ENV MIX_ENV="prod"

# Only copy the final release from the build stage
COPY --from=builder --chown=nobody:root /app/_build/${MIX_ENV}/rel/flaty ./

USER nobody

# If using an environment that doesn't automatically reap zombie processes, it is
# advised to add an init process such as tini via `apt-get install`
# above and adding an entrypoint. See https://github.com/krallin/tini for details
# ENTRYPOINT ["/tini", "--"]

CMD ["/app/bin/server"]

I’d ignore the “Automate the inclusion of build secrets using an ephemeral Machine” portion altogether (at least to start). Explicitly mount all secrets you need access to and use them to make your mix release work.

Compile time environment variables are trickier to use, and depending on what you are doing can make the compiled artifact less portable. Is the value you are wanting to provide at compile time sensitive data? If not, you can use build arguments:

ARG MY_ARG="unknown"
RUN mix compile
flyctl deploy --build-arg MY_ARG=value

I did not have Arg for my env. So now when calling flyctl deploy --build-arg MY_ARG=value value is used.

i still don’t understand why i need to provide value in github actions as well. It already exists in fly.io, but that probably is topic for another day.

Also i understand one should not use compile envs lightly

I’m assuming you mean the values exist as secrets within the fly.io application. The images are built outside of your fly application environment, either locally on the machine that invoked flyctl deploy (if local builds are configured or you added the --local-only flag) or in an “App Builder” which is a separate fly machine from your application. Your organization can have multiple applications, but it’ll just have one App Builder (found at https://fly.io/dashboard/<org>/builders).

1 Like