# syntax=docker/dockerfile:1.4 # 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?name=ubuntu # https://hub.docker.com/_/ubuntu/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/tags?name=trixie-20251229-slim - for the release image # - https://pkgs.org/ - resource for finding needed packages # - Ex: docker.io/hexpm/elixir:1.20.2-erlang-29.0.3-debian-trixie-20260713-slim # ARG ELIXIR_VERSION=1.20.2 ARG OTP_VERSION=29.0.3 ARG DEBIAN_VERSION=trixie-20260713-slim ARG BUILDER_IMAGE="docker.io/hexpm/elixir:${ELIXIR_VERSION}-erlang-${OTP_VERSION}-debian-${DEBIAN_VERSION}" ARG RUNNER_IMAGE="docker.io/debian:${DEBIAN_VERSION}" FROM ${BUILDER_IMAGE} AS builder ENV MIX_OS_DEPS_COMPILE_PARTITION_COUNT=3 # Enable parallel compilation with 3 cores ENV ELIXIR_ERL_OPTIONS="+JPperf true" ENV ERL_AFLAGS="-kernel shell_history enabled" # Disable TTY for cross-architecture builds ENV TERM=dumb # install build dependencies RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get upgrade -y \ && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends build-essential git curl libsnmp-dev \ && rm -rf /var/lib/apt/lists/* # prepare build dir WORKDIR /app # set build ENV ENV MIX_ENV="prod" # install hex + rebar (must use cache mounts so they persist into subsequent cache-mounted steps) RUN --mount=type=cache,target=/root/.hex \ --mount=type=cache,target=/root/.mix \ mix local.hex --force \ && mix local.rebar --force # install mix dependencies COPY mix.exs mix.lock ./ COPY vendor vendor RUN --mount=type=cache,target=/root/.hex \ --mount=type=cache,target=/root/.mix \ 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 --mount=type=cache,target=/root/.hex \ --mount=type=cache,target=/root/.mix \ mix deps.compile RUN --mount=type=cache,target=/root/.mix \ --mount=type=cache,target=/root/.npm \ mix assets.setup COPY priv priv COPY c_src c_src # Compile the C NIF before Elixir compilation RUN cd c_src && make COPY lib lib # Compile the release RUN --mount=type=cache,target=/root/.mix \ mix compile COPY assets assets # compile assets RUN --mount=type=cache,target=/root/.mix \ mix assets.deploy # Changes to config/runtime.exs don't require recompiling the code COPY config/runtime.exs config/ COPY rel rel RUN --mount=type=cache,target=/root/.mix \ 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} AS final RUN apt-get update \ && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends libstdc++6 openssl libncurses6 locales ca-certificates iputils-ping snmp libsnmp40 libsctp1 \ && rm -rf /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/towerops ./ 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", "--"] # Health check for container orchestration HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \ CMD ["/app/bin/towerops", "rpc", "1 + 1"] CMD ["/app/bin/server"]