# syntax=docker/dockerfile:1.6 # # Pre-built runtime base image for the Elixir app. Contains: # * Debian trixie-slim with locale set to en_US.UTF-8 # * Runtime apt deps for the BEAM + Erlang ssl + snmp # * `cdo` + `gdal-bin` (~1 GB dep tree, the slowest install) # * `wgrib2` (compiled here from source against NCEPLIBS-g2c v2.3.0) # # Built by `.forgejo/workflows/build-base.yaml` only when this file or # the workflow changes. The app `Dockerfile` does `FROM prop-base` so # everyday code pushes skip the wgrib2 compile and apt-install entirely. # # Bumping wgrib2 or g2c: change the ARGs below and push — the base # workflow rebuilds and re-tags `:latest`. The app Dockerfile follows # `:latest`, so the next app push picks it up automatically. ARG DEBIAN_VERSION=trixie-20260518-slim ARG RUNNER_IMAGE="docker.io/debian:${DEBIAN_VERSION}" # ---- wgrib2 build stage (cached independently inside this image) ---- FROM ${RUNNER_IMAGE} AS wgrib2-builder ARG WGRIB2_VERSION=3.8.0 # wgrib2 3.6.0 has a memory-corruption bug exposed by HRDPS rotated # lat/lon GRIB2 files plus -lon point extraction (denormal garbage # values, then `free(): invalid size`). Reproduced in the production # pod against an HRDPS sample on 2026-04-30; 3.8.0 fixes it. # g2c v2.3.0 is the minimum wgrib2 3.8.0's CMakeLists demands via # find_package, and the first release where BUILD_G2C defaults ON. ARG G2C_VERSION=2.3.0 RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ rm -f /etc/apt/apt.conf.d/docker-clean \ && apt-get update \ && apt-get install -y --no-install-recommends \ build-essential gfortran cmake wget ca-certificates pkg-config \ zlib1g-dev libaec-dev libpng-dev libopenjp2-7-dev # NCEPLIBS-g2c carries the actual JPEG2000 / PNG / AEC GRIB2 decoders # that wgrib2 dispatches to via USE_G2CLIB_LOW. Jasper is disabled # (CVE-laden, dropped from Debian). WORKDIR /tmp/g2c RUN wget -q --content-disposition "https://github.com/NOAA-EMC/NCEPLIBS-g2c/archive/refs/tags/v${G2C_VERSION}.tar.gz" \ -O g2c.tar.gz \ && tar xzf g2c.tar.gz \ && cd NCEPLIBS-g2c-${G2C_VERSION} \ && mkdir build && cd build \ && cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local \ -DCMAKE_BUILD_TYPE=Release \ -DUSE_PNG=ON \ -DUSE_AEC=ON \ -DUSE_OpenJPEG=ON \ -DUSE_Jasper=OFF \ && make -j$(nproc) \ && make install WORKDIR /tmp/wgrib2 RUN wget -q --content-disposition "https://github.com/NOAA-EMC/wgrib2/archive/refs/tags/v${WGRIB2_VERSION}.tar.gz" \ -O wgrib2.tar.gz \ && tar xzf wgrib2.tar.gz \ && cd wgrib2-${WGRIB2_VERSION} \ && mkdir build && cd build \ && cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release \ -DUSE_AEC=ON -DUSE_G2CLIB_LOW=ON \ && make -j$(nproc) \ && make install \ && strip /usr/local/bin/wgrib2 # ---- Final runtime base ---- FROM ${RUNNER_IMAGE} AS base # Weekly cron passes CACHE_BUST= so the apt layers below # re-execute and pick up Debian security patches. Source-controlled # pushes leave the value at its `none` default, so day-to-day base # rebuilds (Dockerfile.base edits) don't pay the apt-update cost # unless the week has rolled over. # # Note: the registry occasionally GCs base-image blobs that the # manifest still references (saw this 2026-05-04 — `final 1/5 FROM # prop-base:latest` failed with "could not fetch content descriptor # … not found"). When that happens, push any change to this file to # re-trigger the build-base workflow and re-upload the layers. ARG CACHE_BUST=none # Runtime apt deps. cdo + gdal-bin are split into a second RUN so a # transient cdo install failure doesn't invalidate the smaller base # layer above. Referencing `${CACHE_BUST}` in the RUN line changes the # layer hash week-to-week without altering the install set. RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ echo "cache-bust=${CACHE_BUST}" \ && rm -f /etc/apt/apt.conf.d/docker-clean \ && apt-get update \ && apt-get install -y --no-install-recommends \ libstdc++6 openssl libncurses6 locales ca-certificates snmp \ libgfortran5 libaec0 zlib1g libpng16-16t64 libopenjp2-7 libsctp1 RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ echo "cache-bust=${CACHE_BUST}" \ && rm -f /etc/apt/apt.conf.d/docker-clean \ && apt-get update \ && apt-get install -y --no-install-recommends cdo gdal-bin 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 # wgrib2 binary + dynamic g2c lib. ldconfig refreshes the loader cache # so /usr/local/lib is searched at exec time without LD_LIBRARY_PATH. COPY --from=wgrib2-builder /usr/local/bin/wgrib2 /usr/local/bin/wgrib2 COPY --from=wgrib2-builder /usr/local/lib/libg2c.so* /usr/local/lib/ RUN ldconfig