Files
ea2967727c birdnet-go-dev: read ONNX Runtime version from upstream's Dockerfile (#3186)
The bundled ORT must match the onnxruntime_go binding the merged source
compiles against; upstream moves both together in its own Dockerfile
ARG. Reading it from the merged tree removes the separate pin that
went stale in 20261102.1 (ORT 1.25.1 vs binding requesting API 29).

Co-authored-by: BirdNET-Go Addon Builder <addon-builder@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 08:28:15 +01:00

372 lines
15 KiB
Docker

# syntax=docker/dockerfile:1
#============================#
# ALEXBELGIUM'S DOCKERFILE #
# BirdNET-Go (OpenVINO/AMD64)#
#============================#
# _.------.
# _.-` ('>.-`"""-.
# '.--'` _'` _ .--.)
# -' '-.-';` `
# ' - _.' ``'--.
# '---` .-'""``
# /`
#=== Home Assistant Addon ===#
#
# AMD64-only, OpenVINO-enabled build of the BirdNET-Go add-on.
# The binary is compiled from the alexbelgium/birdnet-go fork with the
# OpenVINO inference backend enabled (openvino build tag). The OpenVINO
# 2026.2 runtime libraries are bundled in the image, enabling:
# - Faster CPU inference on modern Intel CPUs (AVX-512/AMX)
# - Intel iGPU inference when /dev/dri is passed from the host
#
# Before compiling, "merge-prs.sh" clones the fork, fast-forwards its main
# onto the tphakala/birdnet-go upstream and merges every OPEN, NON-DRAFT
# pull request on the fly. Home Assistant integration layers (2-6) are then
# applied on top and are byte-for-byte identical to the upstream variant.
#
# Build stages 0-1 mirror tphakala/birdnet-go's Docker/Dockerfile; if
# upstream changes its build (Go/Node versions, task names) sync them
# here. The ONNX Runtime version is read from upstream's Dockerfile.
##########################################################
# 0 Prepare merged BirdNET-Go source tree (fork + PRs) #
##########################################################
FROM alpine/git:latest AS birdnet-source
ARG BIRDNET_FORK=alexbelgium/birdnet-go
ARG BIRDNET_UPSTREAM=tphakala/birdnet-go
# BUILD_DATE changes on every CI run, busting this layer's cache so the fork is
# re-cloned and the open PRs are re-merged "on the fly" on every build.
ARG BUILD_DATE=unknown
RUN apk add --no-cache bash curl jq
COPY merge-prs.sh /usr/local/bin/merge-prs.sh
RUN chmod +x /usr/local/bin/merge-prs.sh && \
echo "Build date marker: ${BUILD_DATE}" && \
BIRDNET_FORK="${BIRDNET_FORK}" BIRDNET_UPSTREAM="${BIRDNET_UPSTREAM}" \
/usr/local/bin/merge-prs.sh /birdnet-src
##########################################################
# 0b Download OpenVINO runtime libraries for amd64 #
##########################################################
FROM alpine:3 AS openvino-libs
ARG OPENVINO_BUILD=2026.2.0.21903.52ddc073857
ARG OPENVINO_RELEASE=2026.2
# Download the x86_64 OpenVINO runtime package and extract runtime/lib/intel64.
# The same build id is used for the C API headers downloaded during compilation
# (see Taskfile.yml OPENVINO_BUILD), ensuring header/runtime version parity.
# We use the arm64 package for headers (arch-independent C declarations) but the
# x86_64 package here for the actual runtime .so files.
#
# OpenVINO links against its own bundled oneTBB (libtbb.so.12, libtbbmalloc*),
# which does NOT live in runtime/lib/intel64 but in runtime/3rdparty/tbb/lib.
# It must be bundled too, otherwise dlopen("libopenvino_c.so") fails at runtime
# with "libtbb.so.12: cannot open shared object file: No such file or directory"
# and the OpenVINO backend silently falls back / GPU planning treats the device
# as unavailable.
RUN apk add --no-cache curl && \
mkdir -p /openvino && \
curl -fsSL \
"https://storage.openvinotoolkit.org/repositories/openvino/packages/${OPENVINO_RELEASE}/linux/openvino_toolkit_ubuntu22_${OPENVINO_BUILD}_x86_64.tgz" \
-o /tmp/openvino.tgz && \
tar -xzf /tmp/openvino.tgz -C /tmp/ --strip-components=1 && \
cd /tmp/runtime/lib/intel64 && \
tar -cf - . | tar -xf - -C /openvino/ && \
# Bundle OpenVINO's own oneTBB runtime (preserving symlinks with cp -a).
# Search the whole 3rdparty tree so the copy survives minor layout changes
# between OpenVINO releases. Fail the build loudly if libtbb is missing,
# since the runtime is unusable without it.
find /tmp/runtime/3rdparty -name 'libtbb*.so*' -exec cp -a {} /openvino/ \; && \
if ! ls /openvino/libtbb.so.12* >/dev/null 2>&1; then \
echo "ERROR: libtbb.so.12 not found in OpenVINO package 3rdparty/tbb" >&2; \
exit 1; \
fi && \
rm -rf /tmp/openvino.tgz /tmp/runtime /tmp/tools /tmp/python \
/tmp/samples /tmp/install_dependencies 2>/dev/null || true
#########################################################################
# 1a Build environment (mirrors tphakala/birdnet-go Docker/Dockerfile) #
#########################################################################
FROM --platform=$BUILDPLATFORM golang:1.27-trixie AS buildenv
ARG BUILD_VERSION
ENV BUILD_VERSION=${BUILD_VERSION:-unknown}
# Install Task and other dependencies
RUN apt-get update -q && apt-get install -q -y \
curl \
git \
sudo \
zip \
gcc-aarch64-linux-gnu && \
rm -rf /var/lib/apt/lists/*
# Install Node.js v24 from NodeSource
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - && \
apt-get install -y nodejs && \
rm -rf /var/lib/apt/lists/*
# Install Task
RUN sh -c "$(curl --location https://taskfile.dev/install.sh)" -- -d -b /usr/local/bin
# Create dev-user for building
RUN groupadd --gid 10001 dev-user && \
useradd --uid 10001 --gid dev-user --shell /bin/bash --create-home dev-user && \
usermod -aG sudo dev-user && \
usermod -aG audio dev-user && \
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \
mkdir -p /home/dev-user/src && \
mkdir -p /home/dev-user/lib && \
mkdir -p /home/dev-user/.cache && \
mkdir -p /home/dev-user/.npm && \
chown -R dev-user:dev-user /home/dev-user
USER dev-user
WORKDIR /home/dev-user/src/BirdNET-Go
# Bring in the merged source tree (fork main + upstream sync + open PRs)
COPY --from=birdnet-source --chown=dev-user /birdnet-src ./
# Pre-build frontend once in the shared buildenv stage
ENV PUPPETEER_SKIP_DOWNLOAD=true
RUN task frontend-build
##################
# 1b Build stage #
##################
FROM --platform=$BUILDPLATFORM buildenv AS build
ARG BUILD_VERSION
ENV BUILD_VERSION=${BUILD_VERSION:-unknown}
# Download ONNX Runtime for amd64 (x64). The version is read from upstream's own
# Dockerfile in the merged source tree, so it always matches the onnxruntime_go
# binding this source is compiled against (upstream moves both together).
RUN ONNXRUNTIME_VERSION="$(sed -n 's/^ARG ONNXRUNTIME_VERSION=//p' Dockerfile)" && \
if [ -z "${ONNXRUNTIME_VERSION}" ]; then \
echo "ERROR: ARG ONNXRUNTIME_VERSION not found in upstream Dockerfile" >&2; \
exit 1; \
fi && \
echo "Downloading ONNX Runtime ${ONNXRUNTIME_VERSION} for x64" && \
curl -fsSL "https://github.com/microsoft/onnxruntime/releases/download/v${ONNXRUNTIME_VERSION}/onnxruntime-linux-x64-${ONNXRUNTIME_VERSION}.tgz" \
-o /tmp/onnxruntime.tgz && \
mkdir -p /tmp/onnxruntime && \
tar -xzf /tmp/onnxruntime.tgz -C /tmp/onnxruntime --strip-components=1 && \
cp /tmp/onnxruntime/lib/libonnxruntime*.so* /home/dev-user/lib/ && \
rm -rf /tmp/onnxruntime /tmp/onnxruntime.tgz
# Build BirdNET-Go for amd64 with OpenVINO enabled.
# OPENVINO=true compiles in the openvino build tag, enabling the OpenVINO
# inference backend (dlopen'd at runtime; falls back to ONNX Runtime when
# libopenvino_c.so is absent). The noembed_linux_amd64 task defaults to
# OPENVINO=true anyway; we make it explicit here for clarity.
RUN --mount=type=cache,target=/go/pkg/mod,uid=10001,gid=10001 \
--mount=type=cache,target=/home/dev-user/.cache/go-build,uid=10001,gid=10001 \
task check-tensorflow && \
echo "Building amd64 with OpenVINO enabled, BUILD_VERSION=${BUILD_VERSION}" && \
BUILD_VERSION="${BUILD_VERSION}" DOCKER_LIB_DIR=/home/dev-user/lib task noembed_linux_amd64 OPENVINO=true
#####################################################
# 1c BirdNET-Go runtime base (mirrors upstream) #
#####################################################
FROM debian:trixie-slim AS birdnet-runtime
# Copy TFLite model files (amd64 uses TFLite; OpenVINO reads them via its
# TFLite frontend plugin bundled in the OpenVINO runtime below)
RUN mkdir -p /models
COPY --from=build /home/dev-user/src/BirdNET-Go/internal/classifier/data/*.tflite /models/
RUN chmod -R a+r /models/*.tflite 2>/dev/null || true && \
chmod a+x /models
# Install ALSA library, SOX for audio processing, and system utilities.
# ocl-icd-libopencl1 provides the OpenCL ICD loader; combined with
# /dev/dri passthrough from the host it enables Intel iGPU inference.
RUN apt-get update -q && apt-get install -q -y --no-install-recommends \
adduser \
ca-certificates \
libasound2 \
ffmpeg \
sox \
libsox-fmt-mp3 \
procps \
iproute2 \
net-tools \
curl \
wget \
nano \
vim \
less \
tzdata \
tzdata-legacy \
jq \
strace \
lsof \
bash-completion \
gosu \
ocl-icd-libopencl1 \
&& rm -rf /var/lib/apt/lists/*
# Copy TensorFlow Lite library from build stage
COPY --from=build /home/dev-user/lib/libtensorflowlite_c.so* /usr/lib/
# Copy ONNX Runtime libraries from build stage
COPY --from=build /home/dev-user/lib/libonnxruntime*.so* /usr/lib/
# Copy OpenVINO runtime libraries (libopenvino_c.so, CPU/GPU plugins,
# TFLite frontend, plugins.xml, bundled oneTBB libtbb.so.12, etc.) into a
# dedicated directory and register it with ldconfig so both
# dlopen("libopenvino_c.so") and its libtbb.so.12 dependency resolve.
# plugins.xml is co-located with libopenvino.so so device plugin
# discovery works without additional environment variables.
COPY --from=openvino-libs /openvino/ /usr/lib/openvino/
RUN echo "/usr/lib/openvino" > /etc/ld.so.conf.d/openvino.conf && ldconfig
# Include reset_auth tool from build stage
COPY --from=build /home/dev-user/src/BirdNET-Go/reset_auth.sh /usr/bin/
RUN chmod +x /usr/bin/reset_auth.sh
# Add entrypoint and startup wrapper scripts from build stage
COPY --from=build /home/dev-user/src/BirdNET-Go/Docker/entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
COPY --from=build /home/dev-user/src/BirdNET-Go/Docker/startup-wrapper.sh /usr/bin/
RUN chmod +x /usr/bin/startup-wrapper.sh
# Create config and data directories with proper permissions for rootless compatibility
RUN mkdir -p /config /data/clips /data/models && \
chmod 777 /config /data /data/clips /data/models
# Install the freshly built BirdNET-Go binary
COPY --from=build /home/dev-user/src/BirdNET-Go/bin /usr/bin/
# Enable openvino
ARG NEO_VERSION=26.22.38646.4
ARG IGC_VERSION=2.36.3+21719
RUN groupadd -g 109 render || true && \
usermod -aG render root && \
\
apt-get update -q && apt-get install -q -y --no-install-recommends wget ocl-icd-libopencl1 libdrm2 libdrm-intel1 && \
mkdir -p /tmp/neo && cd /tmp/neo && \
wget -q https://github.com/intel/intel-graphics-compiler/releases/download/v2.36.3/intel-igc-core-2_${IGC_VERSION}_amd64.deb \
https://github.com/intel/intel-graphics-compiler/releases/download/v2.36.3/intel-igc-opencl-2_${IGC_VERSION}_amd64.deb \
https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/intel-opencl-icd_${NEO_VERSION}-0_amd64.deb \
https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/libigdgmm12_22.10.0_amd64.deb \
https://github.com/intel/compute-runtime/releases/download/${NEO_VERSION}/libze-intel-gpu1_${NEO_VERSION}-0_amd64.deb && \
dpkg -i *.deb && \
rm -rf /tmp/neo && \
rm -rf /var/lib/apt/lists/*
#============================================================================#
# From here on, the Home Assistant integration layers are identical to the #
# standard alexbelgium/birdnet-go add-on (which builds FROM the prebuilt #
# upstream image). The only difference above is that the base is compiled #
# from the fork + open PRs with OpenVINO enabled. #
#============================================================================#
##################
# 2 Modify Image #
##################
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# Set S6 wait time
ENV S6_CMD_WAIT_FOR_SERVICES=1 \
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
S6_SERVICES_GRACETIME=0
##################
# 3 Install apps #
##################
# Add rootfs
COPY rootfs/ /
RUN find . -type f \( -name "*.sh" -o -name "run" -o -name "finish" \) -print -exec chmod +x {} \;
# Silence "chmod: Read-only file system" noise from upstream entrypoint:
# Home Assistant mounts /dev/snd read-only, so the chmod is a no-op and the
# stderr output is just noise. The "|| true" already absorbs the exit code.
# Warn (without failing the build) if the upstream pattern drifts so we notice
# in CI logs and can update the patch.
RUN if [ -f /usr/bin/entrypoint.sh ]; then \
if grep -q 'chmod -R a+rw /dev/snd || true' /usr/bin/entrypoint.sh; then \
sed -i 's#chmod -R a+rw /dev/snd || true#chmod -R a+rw /dev/snd 2>/dev/null || true#' /usr/bin/entrypoint.sh; \
else \
echo "WARN: upstream entrypoint.sh /dev/snd chmod pattern not found; nightly drift?" >&2; \
fi; \
fi
# Uses /bin for compatibility purposes
# hadolint ignore=DL4005
RUN if [ ! -f /bin/sh ] && [ -f /usr/bin/sh ]; then ln -s /usr/bin/sh /bin/sh; fi && \
if [ ! -f /bin/bash ] && [ -f /usr/bin/bash ]; then ln -s /usr/bin/bash /bin/bash; fi
# Modules
ARG MODULES="00-local_mounts.sh 00-smb_mounts.sh"
# Automatic modules download
COPY ha_automodules.sh /ha_automodules.sh
RUN chmod 744 /ha_automodules.sh && /ha_automodules.sh "$MODULES" && rm /ha_automodules.sh
# Manual apps
ENV PACKAGES="alsa-utils libasound2-plugins mariadb-client nginx yq"
# Automatic apps & bashio
COPY ha_autoapps.sh /ha_autoapps.sh
RUN chmod 744 /ha_autoapps.sh && /ha_autoapps.sh "$PACKAGES" && rm /ha_autoapps.sh
################
# 4 Entrypoint #
################
# Add entrypoint
ENV S6_STAGE2_HOOK=/ha_entrypoint.sh
COPY ha_entrypoint.sh /ha_entrypoint.sh
RUN chmod 777 /ha_entrypoint.sh
# Install bashio
COPY bashio-standalone.sh /usr/local/lib/bashio-standalone.sh
RUN chmod 0755 /usr/local/lib/bashio-standalone.sh
# Avoid config.yaml interference
RUN sed -i "s|config.yaml|config_env.yaml|g" /etc/cont-init.d/01-config_yaml.sh
ENTRYPOINT [ "/usr/bin/env" ]
CMD [ "/ha_entrypoint.sh" ]
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
############
# 5 Labels #
############
ARG BUILD_ARCH
ARG BUILD_DATE
ARG BUILD_DESCRIPTION
ARG BUILD_NAME
ARG BUILD_REF
ARG BUILD_REPOSITORY
ARG BUILD_VERSION
ENV BUILD_VERSION="${BUILD_VERSION}"
LABEL \
io.hass.name="${BUILD_NAME}" \
io.hass.description="${BUILD_DESCRIPTION}" \
io.hass.arch="${BUILD_ARCH}" \
io.hass.type="addon" \
io.hass.version=${BUILD_VERSION} \
maintainer="alexbelgium (https://github.com/alexbelgium)" \
org.opencontainers.image.title="${BUILD_NAME}" \
org.opencontainers.image.description="${BUILD_DESCRIPTION}" \
org.opencontainers.image.vendor="Home Assistant Add-ons" \
org.opencontainers.image.authors="alexbelgium (https://github.com/alexbelgium)" \
org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.url="https://github.com/alexbelgium" \
org.opencontainers.image.source="https://github.com/${BUILD_REPOSITORY}" \
org.opencontainers.image.documentation="https://github.com/${BUILD_REPOSITORY}/blob/main/README.md" \
org.opencontainers.image.created=${BUILD_DATE} \
org.opencontainers.image.revision=${BUILD_REF} \
org.opencontainers.image.version=${BUILD_VERSION}
#################
# 6 Healthcheck #
#################