-
-
Notifications
You must be signed in to change notification settings - Fork 40
Expand file tree
/
Copy pathDockerfile
More file actions
309 lines (275 loc) · 14.1 KB
/
Dockerfile
File metadata and controls
309 lines (275 loc) · 14.1 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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# Stage 1: Build image
ARG DEBIAN_SUITE=sid
ARG SQLITE_YEAR=2026
ARG SQLITE_AUTOCONF_VERSION=3520000
ARG LIBUV_VERSION=1.52.1
ARG LLHTTP_VERSION=9.3.1
ARG DEB_BUILD=false
FROM debian:${DEBIAN_SUITE}-slim AS builder
ARG DEBIAN_SUITE
ARG SQLITE_YEAR
ARG SQLITE_AUTOCONF_VERSION
ARG LIBUV_VERSION
ARG LLHTTP_VERSION
ARG DEB_BUILD
# Set non-interactive mode
ENV DEBIAN_FRONTEND=noninteractive
# Install build dependencies including Node.js and FFmpeg dev libraries
# sid ships Go 1.26+/Node 22.x/FFmpeg 8.x; trixie ships Go 1.24+/Node 20.x/FFmpeg 7.x
#
# Pre-install systemd-standalone-sysusers to satisfy the sysusers virtual
# dependency without pulling in the full systemd package. The full systemd
# postinst crashes under QEMU ARM emulation (SIGSEGV in systemd 260.x),
# breaking all cross-architecture builds.
RUN apt-get update && \
apt-get install -y --no-install-recommends systemd-standalone-sysusers && \
apt-get install -y \
git cmake build-essential pkg-config file \
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev \
libcurl4-openssl-dev \
libmbedtls-dev curl wget ca-certificates gpg libcjson-dev \
libmosquitto-dev \
libyaml-dev \
nodejs npm \
golang-go && \
# Verify installation
node --version && \
npm --version && \
go version && \
rm -rf /var/lib/apt/lists/*
# For .deb builds: use system dev packages instead of building from source.
# This ensures the binary links against system SONAMEs so that libuv, libsqlite3,
# and libllhttp can be proper package dependencies instead of bundled libraries.
RUN if [ "$DEB_BUILD" = "true" ]; then \
apt-get update && apt-get install -y --no-install-recommends \
libuv1-dev libsqlite3-dev libllhttp-dev sqlite3 && \
rm -rf /var/lib/apt/lists/* && \
ARCH=$(uname -m) && \
case $ARCH in \
x86_64) LIBDIR="/usr/lib/x86_64-linux-gnu" ;; \
aarch64) LIBDIR="/usr/lib/aarch64-linux-gnu" ;; \
armv7l) LIBDIR="/usr/lib/arm-linux-gnueabihf" ;; \
*) echo "Unsupported architecture: $ARCH"; exit 1 ;; \
esac && \
cp -a ${LIBDIR}/libuv.so* /usr/lib/ && \
cp -a ${LIBDIR}/libsqlite3.so* /usr/lib/ && \
cp -a ${LIBDIR}/libllhttp.so* /usr/lib/; \
fi
# Build upstream SQLite (skipped for .deb builds which use system libsqlite3)
RUN if [ "$DEB_BUILD" != "true" ]; then \
cd /tmp && \
wget -q "https://www.sqlite.org/${SQLITE_YEAR}/sqlite-autoconf-${SQLITE_AUTOCONF_VERSION}.tar.gz" && \
tar -xzf "sqlite-autoconf-${SQLITE_AUTOCONF_VERSION}.tar.gz" && \
cd "sqlite-autoconf-${SQLITE_AUTOCONF_VERSION}" && \
./configure --prefix=/usr --disable-static && \
make -j"$(nproc)" && \
make install && \
sqlite3 --version; \
fi
# Build upstream libuv (skipped for .deb builds which use system libuv)
RUN if [ "$DEB_BUILD" != "true" ]; then \
cd /tmp && \
wget -q "https://github.com/libuv/libuv/archive/refs/tags/v${LIBUV_VERSION}.tar.gz" -O libuv.tar.gz && \
tar -xzf libuv.tar.gz && \
cd "libuv-${LIBUV_VERSION}" && \
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF -DCMAKE_INSTALL_PREFIX=/usr && \
cmake --build build -j"$(nproc)" && \
cmake --install build && \
ARCH=$(uname -m) && \
case $ARCH in \
x86_64) LIBUV_DIR="/usr/lib/x86_64-linux-gnu" ;; \
aarch64) LIBUV_DIR="/usr/lib/aarch64-linux-gnu" ;; \
armv7l) LIBUV_DIR="/usr/lib/arm-linux-gnueabihf" ;; \
*) echo "Unsupported architecture: $ARCH"; exit 1 ;; \
esac && \
cp -a "$LIBUV_DIR"/libuv.so* /usr/lib/ && \
pkg-config --modversion libuv; \
fi
# Build upstream llhttp (skipped for .deb builds which use system libllhttp)
RUN if [ "$DEB_BUILD" != "true" ]; then \
mkdir -p /tmp/llhttp/include /tmp/llhttp/src /usr/include && \
wget -q "https://raw.githubusercontent.com/nodejs/llhttp/release/include/llhttp.h" -O /tmp/llhttp/include/llhttp.h && \
wget -q "https://raw.githubusercontent.com/nodejs/llhttp/release/src/llhttp.c" -O /tmp/llhttp/src/llhttp.c && \
wget -q "https://raw.githubusercontent.com/nodejs/llhttp/release/src/api.c" -O /tmp/llhttp/src/api.c && \
wget -q "https://raw.githubusercontent.com/nodejs/llhttp/release/src/http.c" -O /tmp/llhttp/src/http.c && \
cc -fPIC -I/tmp/llhttp/include -c /tmp/llhttp/src/llhttp.c -o /tmp/llhttp/llhttp.o && \
cc -fPIC -I/tmp/llhttp/include -c /tmp/llhttp/src/api.c -o /tmp/llhttp/api.o && \
cc -fPIC -I/tmp/llhttp/include -c /tmp/llhttp/src/http.c -o /tmp/llhttp/http.o && \
cc -shared -Wl,-soname,libllhttp.so.9 -o /usr/lib/libllhttp.so.${LLHTTP_VERSION} /tmp/llhttp/llhttp.o /tmp/llhttp/api.o /tmp/llhttp/http.o && \
ln -sf /usr/lib/libllhttp.so.${LLHTTP_VERSION} /usr/lib/libllhttp.so.9 && \
ln -sf /usr/lib/libllhttp.so.${LLHTTP_VERSION} /usr/lib/libllhttp.so && \
install -m 644 /tmp/llhttp/include/llhttp.h /usr/include/llhttp.h && \
printf 'prefix=/usr\nexec_prefix=${prefix}\nlibdir=${prefix}/lib\nincludedir=${prefix}/include\n\nName: libllhttp\nDescription: llhttp parser\nVersion: %s\nLibs: -L${libdir} -lllhttp\nCflags: -I${includedir}\n' "$LLHTTP_VERSION" > /usr/lib/pkgconfig/libllhttp.pc && \
pkg-config --modversion libllhttp; \
fi
# Fetch external dependencies
RUN mkdir -p /opt/external && \
# ezxml
cd /opt/external && \
git clone https://github.com/lxfontes/ezxml.git && \
# inih
cd /opt/external && \
git clone https://github.com/benhoyt/inih.git
# Copy current directory contents into container
WORKDIR /opt
COPY . .
# Create pkg-config files for MbedTLS with architecture-specific paths
RUN mkdir -p /usr/lib/pkgconfig && \
ARCH=$(uname -m) && \
MBEDTLS_VERSION=$(dpkg-query -W -f='${Version}' libmbedtls-dev | cut -d- -f1) && \
case $ARCH in \
x86_64) LIB_DIR="/usr/lib/x86_64-linux-gnu" ;; \
aarch64) LIB_DIR="/usr/lib/aarch64-linux-gnu" ;; \
armv7l) LIB_DIR="/usr/lib/arm-linux-gnueabihf" ;; \
*) echo "Unsupported architecture: $ARCH"; exit 1 ;; \
esac && \
echo "prefix=/usr\nexec_prefix=\${prefix}\nlibdir=$LIB_DIR\nincludedir=\${prefix}/include\n\nName: mbedtls\nDescription: MbedTLS Library\nVersion: $MBEDTLS_VERSION\nLibs: -L\${libdir} -lmbedtls\nCflags: -I\${includedir}" > /usr/lib/pkgconfig/mbedtls.pc && \
echo "prefix=/usr\nexec_prefix=\${prefix}\nlibdir=$LIB_DIR\nincludedir=\${prefix}/include\n\nName: mbedcrypto\nDescription: MbedTLS Crypto Library\nVersion: $MBEDTLS_VERSION\nLibs: -L\${libdir} -lmbedcrypto\nCflags: -I\${includedir}" > /usr/lib/pkgconfig/mbedcrypto.pc && \
echo "prefix=/usr\nexec_prefix=\${prefix}\nlibdir=$LIB_DIR\nincludedir=\${prefix}/include\n\nName: mbedx509\nDescription: MbedTLS X509 Library\nVersion: $MBEDTLS_VERSION\nLibs: -L\${libdir} -lmbedx509\nCflags: -I\${includedir}" > /usr/lib/pkgconfig/mbedx509.pc && \
chmod 644 /usr/lib/pkgconfig/mbedtls.pc /usr/lib/pkgconfig/mbedcrypto.pc /usr/lib/pkgconfig/mbedx509.pc
# Build go2rtc from local submodule (AlexxIT/go2rtc v1.9.14)
# Go 1.26 is installed from Debian sid packages
RUN mkdir -p /bin /etc/lightnvr/go2rtc && \
# Build go2rtc from local submodule (already copied by COPY . .)
cd /opt/go2rtc && \
GOTOOLCHAIN=auto go mod tidy && \
GOTOOLCHAIN=auto CGO_ENABLED=0 go build -ldflags "-s -w" -trimpath -o /bin/go2rtc . && \
chmod +x /bin/go2rtc && \
# Create basic configuration file
echo "# go2rtc configuration file" > /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo "api:" >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo " listen: :1984" >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo " base_path: /go2rtc" >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo ' origin: "*"' >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo "" >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo "webrtc:" >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo " ice_servers:" >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo " - urls: [stun:stun.l.google.com:19302]" >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo "" >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo "log:" >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo " level: info" >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo "" >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo "streams:" >> /etc/lightnvr/go2rtc/go2rtc.yaml && \
echo " # Streams will be added dynamically by LightNVR" >> /etc/lightnvr/go2rtc/go2rtc.yaml
# Make a slight modification to the install script to skip systemd
RUN if grep -q "systemctl" scripts/install.sh; then \
sed -i 's/systemctl/#systemctl/g' scripts/install.sh; \
fi
# Generate version.js before building web assets (it is not checked into git)
RUN ./scripts/extract_version.sh
# Build web assets using Vite
RUN echo "Building web assets..." && \
# Verify Node.js and npm are available
node --version && \
npm --version && \
cd /opt/web && \
# Install npm dependencies (use --ignore-scripts to skip chromedriver install which doesn't support ARM)
npm ci --ignore-scripts && \
# Build web assets
npm run build && \
# Verify build output exists
ls -la dist/ && \
echo "Web assets built successfully"
# Clean any existing build files and build the application with go2rtc support
RUN mkdir -p /etc/lightnvr /var/lib/lightnvr/data /var/log/lightnvr /var/run/lightnvr && \
chmod -R 777 /var/lib/lightnvr /var/log/lightnvr /var/run/lightnvr && \
# Clean any existing build files
rm -rf build/ && \
# Determine architecture-specific pkgconfig path
ARCH=$(uname -m) && \
case $ARCH in \
x86_64) PKG_CONFIG_ARCH_PATH="/usr/lib/x86_64-linux-gnu/pkgconfig" ;; \
aarch64) PKG_CONFIG_ARCH_PATH="/usr/lib/aarch64-linux-gnu/pkgconfig" ;; \
armv7l) PKG_CONFIG_ARCH_PATH="/usr/lib/arm-linux-gnueabihf/pkgconfig" ;; \
*) echo "Unsupported architecture: $ARCH"; exit 1 ;; \
esac && \
# Build the application with go2rtc and SOD dynamic linking
PKG_CONFIG_PATH=/usr/lib/pkgconfig:$PKG_CONFIG_ARCH_PATH:$PKG_CONFIG_PATH \
./scripts/build.sh --release --with-sod --sod-dynamic --with-go2rtc --go2rtc-binary=/bin/go2rtc --go2rtc-config-dir=/etc/lightnvr/go2rtc --go2rtc-api-port=1984 && \
./scripts/install.sh --prefix=/ --with-go2rtc --go2rtc-config-dir=/etc/lightnvr/go2rtc --without-systemd
# Stage 2: Minimal runtime image
FROM debian:${DEBIAN_SUITE}-slim AS runtime
ARG DEBIAN_SUITE
ARG SQLITE_YEAR
ARG SQLITE_AUTOCONF_VERSION
ENV DEBIAN_FRONTEND=noninteractive
# Install only necessary runtime dependencies
# ffmpeg pulls the correct versioned libavcodec/libavformat/libavutil/libswscale
# for the target suite (e.g. libavcodec62 on sid, libavcodec61 on trixie)
RUN apt-get update && apt-get install -y --no-install-recommends \
ffmpeg \
libcurl4t64 libmbedtls21 libmbedcrypto16 procps curl ca-certificates \
libmosquitto1 \
libyaml-0-2 && \
rm -rf /var/lib/apt/lists/*
# Create directory structure
RUN mkdir -p \
/usr/share/lightnvr/models \
/etc/lightnvr \
/etc/lightnvr/go2rtc \
/var/lib/lightnvr \
/var/lib/lightnvr/www \
/var/log/lightnvr \
/var/run/lightnvr && \
chmod -R 755 /var/lib/lightnvr /var/log/lightnvr /var/run/lightnvr
# Copy binaries from builder
COPY --from=builder /bin/lightnvr /bin/lightnvr
COPY --from=builder /bin/go2rtc /bin/go2rtc
COPY --from=builder /usr/bin/sqlite3 /usr/bin/sqlite3
COPY --from=builder /usr/lib/libuv.so* /usr/lib/
COPY --from=builder /usr/lib/libllhttp.so* /usr/lib/
# Copy latest upstream SQLite shared library built in the builder stage
COPY --from=builder /usr/lib/libsqlite3.so* /usr/lib/
# Copy SOD libraries (use /usr/lib/ consistently; on usrmerge systems /lib → /usr/lib)
COPY --from=builder /usr/lib/libsod.so* /usr/lib/
# Copy web assets (copy CONTENTS of dist into /var/lib/lightnvr/www)
COPY --from=builder /opt/web/dist/ /var/lib/lightnvr/www/
# Copy database migrations
COPY --from=builder /opt/db/migrations/ /usr/share/lightnvr/migrations/
# Copy entrypoint script
COPY docker-entrypoint.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
# Create a startup script to launch both services
RUN echo '#!/bin/bash' > /bin/start.sh && \
echo 'set -e' >> /bin/start.sh && \
echo '' >> /bin/start.sh && \
echo '# Trap to ensure cleanup on exit' >> /bin/start.sh && \
echo 'cleanup() {' >> /bin/start.sh && \
echo ' echo "Cleaning up processes..."' >> /bin/start.sh && \
echo ' kill $GO2RTC_PID 2>/dev/null || true' >> /bin/start.sh && \
echo ' wait $GO2RTC_PID 2>/dev/null || true' >> /bin/start.sh && \
echo '}' >> /bin/start.sh && \
echo 'trap cleanup EXIT' >> /bin/start.sh && \
echo '' >> /bin/start.sh && \
echo '# Start go2rtc in the background' >> /bin/start.sh && \
echo '/bin/go2rtc --config /etc/lightnvr/go2rtc/go2rtc.yaml &' >> /bin/start.sh && \
echo 'GO2RTC_PID=$!' >> /bin/start.sh && \
echo '' >> /bin/start.sh && \
echo '# Wait a moment for go2rtc to start' >> /bin/start.sh && \
echo 'sleep 2' >> /bin/start.sh && \
echo '' >> /bin/start.sh && \
echo '# Start lightnvr in the foreground' >> /bin/start.sh && \
echo '# When lightnvr exits (including for restart), this script will exit' >> /bin/start.sh && \
echo '# and the container orchestrator will restart the entire container' >> /bin/start.sh && \
echo '/bin/lightnvr -c /etc/lightnvr/lightnvr.ini' >> /bin/start.sh && \
echo 'LIGHTNVR_EXIT_CODE=$?' >> /bin/start.sh && \
echo '' >> /bin/start.sh && \
echo '# Cleanup will be called by the trap' >> /bin/start.sh && \
echo 'exit $LIGHTNVR_EXIT_CODE' >> /bin/start.sh && \
chmod +x /bin/start.sh
# Define volumes for persistent data only
# Note: Do NOT mount /var/lib/lightnvr directly as it will overwrite web assets
VOLUME ["/etc/lightnvr", "/var/lib/lightnvr/data"]
# Expose ports
EXPOSE 8080 8554 8555 8555/udp 1984
# Environment variables for configuration
ENV GO2RTC_CONFIG_PERSIST=true \
LIGHTNVR_AUTO_INIT=true \
LIGHTNVR_WEB_ROOT=/var/lib/lightnvr/www
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/ || exit 1
# Use entrypoint script for initialization
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
# Command to start the services
CMD ["/bin/start.sh"]