-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
176 lines (147 loc) · 6.4 KB
/
Dockerfile
File metadata and controls
176 lines (147 loc) · 6.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# Stage 1: Create a clean git repository with current working files
# This approach:
# - Uses the local .git to preserve remote configuration
# - Includes uncommitted changes from working directory
# - Creates a minimal git repo without bloated history
# - Allows testing local changes without committing first
FROM alpine/git:v2.45.2 AS git-stage
WORKDIR /source
# Copy everything including .git (temporarily, just for this stage)
COPY . /source/
# Create a new repo with just the working directory state
WORKDIR /repo
# Git repository URL (can be overridden at build time)
# Placed here to minimize cache invalidation - only affects the RUN command below
ARG GIT_REPOSITORY_URL=https://github.com/mverteuil/BirdNET-Pi.git
# Optimized: Combine all git operations in a single layer
RUN cp -r /source/* . 2>/dev/null || true && \
cp -r /source/.??* . 2>/dev/null || true && \
rm -rf .git && \
git init && \
git --git-dir=/source/.git remote get-url origin > /tmp/remote_url 2>/dev/null || echo "${GIT_REPOSITORY_URL}" > /tmp/remote_url && \
git remote add origin "$(cat /tmp/remote_url)" && \
git add -A && \
git config user.email "docker@build" && \
git config user.name "Docker Build" && \
git commit -m "Docker build snapshot with working directory changes" && \
git config --add safe.directory /repo && \
rm -f /tmp/remote_url
# Stage 2: Main application (runtime)
FROM ghcr.io/astral-sh/uv:python3.11-bookworm-slim AS runtime
# Asset version for runtime downloads (can be overridden via environment variable)
ARG BIRDNET_ASSETS_VERSION=latest
# Combine ENV declarations for better layer efficiency
ENV DNS_SERVER=8.8.8.8 \
DEBIAN_FRONTEND=noninteractive \
PYTHONUNBUFFERED=1 \
UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
MPLCONFIGDIR=/var/lib/birdnetpi/config \
BIRDNETPI_APP=/opt/birdnetpi \
BIRDNETPI_DATA=/var/lib/birdnetpi \
BIRDNETPI_CONFIG=/var/lib/birdnetpi/config/birdnetpi.yaml \
SYSLOG_SERVER=localhost \
SYSLOG_PORT=514 \
SYSLOG_PROTO=udp
# Set shell for pipefail
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# OPTIMIZATION: Combine package installation and cleanup in single layer
# This reduces the image size by ~100MB
RUN apt-get update && \
apt-get install -y --no-install-recommends \
alsa-utils \
apt-transport-https \
avahi-utils \
bc \
ca-certificates \
caddy \
curl \
debian-archive-keyring \
debian-keyring \
git \
gnupg \
icecast2 \
iproute2 \
libjpeg-dev \
libportaudio2 \
libsox-fmt-mp3 \
lsof \
redis-server \
net-tools \
portaudio19-dev \
pulseaudio \
python3-systemd \
python3-venv \
sox \
sqlite3 \
supervisor \
zlib1g-dev && \
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg && \
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
# Remove unnecessary files to reduce size
find /usr/share/doc -depth -type f ! -name copyright -delete && \
find /usr/share/doc -empty -delete && \
rm -rf /usr/share/man/* /usr/share/groff/* /usr/share/info/*
# Copy service configuration files with proper permissions
COPY --chmod=644 config_templates/Caddyfile /etc/caddy/Caddyfile
COPY --chmod=644 config_templates/supervisord.conf /etc/supervisor/supervisord.conf
COPY --chmod=744 config_templates/supervisor-wrapper.py /usr/local/bin/supervisor-wrapper.py
# Create birdnetpi user and set up necessary directories
RUN useradd -m -s /bin/bash birdnetpi && \
usermod -aG audio,video,dialout birdnetpi && \
mkdir -p /var/run/supervisor /opt/birdnetpi /var/lib/birdnetpi/{config,models,recordings,database} && \
chown -R birdnetpi:birdnetpi /var/run/supervisor /opt/birdnetpi /var/lib/birdnetpi && \
chmod 777 /var/run/supervisor
# Switch to birdnetpi user for all application-related operations
USER birdnetpi
WORKDIR /opt/birdnetpi
# Install the project's dependencies using the lockfile and settings
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --locked --no-install-project --no-dev
# Copy the project source code with clean git repository from stage 1
# This gives us git functionality without the bloated history
COPY --from=git-stage --chown=birdnetpi:birdnetpi /repo /opt/birdnetpi
# Install the project
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --locked --no-dev
# Configure git safe directory for the birdnetpi user
RUN git config --global --add safe.directory /opt/birdnetpi
# Set the asset version as an environment variable for runtime use
ENV BIRDNET_ASSETS_VERSION=${BIRDNET_ASSETS_VERSION}
# Assets are now downloaded at runtime via init container
# This reduces image size and leverages persistent volumes
# Add the BirdNET-Pi virtual environment to the PATH
ENV PATH="/opt/birdnetpi/.venv/bin:${PATH}"
# Health check for container orchestration
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Expose the port for Caddy (8000)
EXPOSE 8000
# Configure Docker to use JSON logging
LABEL logging=json
# Run supervisord with the wrapper script for emitting JSON logs
CMD ["/usr/local/bin/supervisor-wrapper.py"]
# ============================================================================
# Profiling stage - extends runtime with profiling tools
# ============================================================================
FROM runtime AS profiling
# Install pyinstrument for profiling capabilities
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --locked --group profiling
# ============================================================================
# Init stage - for permission setup (runs as root)
# ============================================================================
FROM runtime AS init
# Switch back to root for init operations
# hadolint ignore=DL3002
USER root
# Init container entrypoint script
COPY --chmod=700 config_templates/container-init.sh /init.sh
# Use the init script as entrypoint
ENTRYPOINT ["/init.sh"]