Naut/Dockerfile
ookami125 8ca1cf3371 docker: run as configurable PUID/PGID (default 99:100 for unraid)
The image ran as a hardcoded uid 1000, which doesn't match NAS shares
(unraid files are nobody:users = 99:100), so moving completed files onto
the share failed with EACCES (surfaced as a generic "i/o error").

Drop the baked-in user and instead start the entrypoint as root, chown
the daemon's own state (/data, /run/naut) to PUID:PGID, then gosu down to
that uid:gid before exec'ing nautd. The downloads share is left untouched
so its ownership comes from the host/NFS export. PUID/PGID default to
99:100 so writes to /mnt/user shares work out of the box; override per
your storage owner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 01:14:37 -04:00

135 lines
5.7 KiB
Docker

# Naut-Torrent — multi-stage build.
#
# The daemon, the webui plugin, and the static UI assets are all compiled from
# source inside the image, so the binaries are frozen at `docker build` time:
# a rebuild is the only thing that bumps the running version.
#
# docker build -t naut-torrent .
# docker run -d --name naut \
# -p 8080:8080 -p 6881:6881 -p 6881:6881/udp \
# -e NAUT_AUTH_PASSWORD=change-me \
# -v naut-data:/data -v /path/to/downloads:/downloads \
# naut-torrent
#
# The build context must be the Naut-Torrent repo with its submodules checked
# out (external/torrent-peer, external/torrent-tracker, web/torrent-ui):
# git submodule update --init --recursive
# ---------------------------------------------------------------------------
# Stage 1 — build
# ---------------------------------------------------------------------------
FROM debian:bookworm-slim AS build
# Toolchain + library headers. Lua/Jansson/SQLite/OpenSSL/liburing are linked
# from the distro (the project's default, non-vendored build path).
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
cmake \
pkg-config \
liburing-dev \
libssl-dev \
libjansson-dev \
liblua5.4-dev \
libsqlite3-dev \
&& rm -rf /var/lib/apt/lists/*
# CMake asks pkg-config for the module name "lua"; Debian ships it as "lua5.4".
# Bridge the two with a .pc symlink rather than patching CMakeLists.
RUN set -eux; \
luapc="$(find /usr -name 'lua5.4.pc' | head -n1)"; \
test -n "$luapc"; \
mkdir -p /usr/local/lib/pkgconfig; \
ln -sf "$luapc" /usr/local/lib/pkgconfig/lua.pc
ENV PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
WORKDIR /src
COPY . .
# Configure + build only the artifacts the image ships. -DNAUT_STANDALONE=OFF
# (default) uses the system Lua/Jansson installed above.
RUN cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \
&& cmake --build build -j"$(nproc)" --target nautd nautctl naut_webui
# ---------------------------------------------------------------------------
# Stage 2 — runtime
# ---------------------------------------------------------------------------
FROM debian:bookworm-slim AS runtime
# Shared libraries the daemon + plugin load at runtime, CA certs for HTTPS RSS
# feeds / tracker announces, and gosu to drop privileges to PUID/PGID at start.
RUN apt-get update && apt-get install -y --no-install-recommends \
liburing2 \
libssl3 \
libjansson4 \
liblua5.4-0 \
libsqlite3-0 \
ca-certificates \
gosu \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Binaries, the webui plugin, and the static frontend assets.
COPY --from=build /src/build/nautd /app/nautd
COPY --from=build /src/build/nautctl /usr/local/bin/nautctl
COPY --from=build /src/build/naut_webui.so /app/naut_webui.so
COPY --from=build /src/web/torrent-ui/public /app/web/torrent-ui/public
# nautd links the two submodule engines as shared libraries; ship them and
# refresh the loader cache so the SONAMEs resolve at runtime.
COPY --from=build /src/build/torrent-peer/libtorrentpeer.so /usr/local/lib/
COPY --from=build /src/build/torrent-tracker/libtorrenttracker.so /usr/local/lib/
RUN ldconfig
# Bundled Lua scripts (anime_sort.lua and friends). Loaded by default via
# NAUT_SCRIPT below; the daemon runs one script at a time.
COPY --from=build /src/examples /app/scripts
# Defaults — override any of these at `docker run` time with -e.
ENV NAUT_WEBUI_HOST=0.0.0.0 \
NAUT_WEBUI_PORT=8080 \
NAUT_WEBUI_ROOT=/app/web/torrent-ui/public \
NAUT_WEBUI_DB=/data/webui.db \
NAUT_WEBUI_SAVE_PATH=/downloads \
NAUT_STATE_DIR=/data/state \
NAUT_SOCKET=/run/naut/nautd.sock \
PUID=99 \
PGID=100
# PUID / PGID # uid:gid the daemon runs as. Default 99:100
# # = unraid's nobody:users, so it can write to
# # /mnt/user shares. Match your storage owner.
# NAUT_AUTH_USER=admin # default
# NAUT_AUTH_PASSWORD=... # generated + printed to the log if unset
# NAUT_SESSION_TTL=604800 # login lifetime in seconds (default 7d)
# NAUT_SCRIPT=/app/scripts/anime_sort.lua # set to load a script at startup
# Entrypoint: start as root, create the data/socket dirs and hand the daemon's
# own state (not the downloads share) to PUID:PGID, then drop privileges with
# gosu and exec nautd. Built with printf (single-quoted lines keep the $VARs
# literal) so the image stays a single Dockerfile on any builder.
RUN printf '%s\n' \
'#!/bin/sh' \
'set -e' \
': "${PUID:=99}" "${PGID:=100}"' \
'mkdir -p "$NAUT_STATE_DIR" "$(dirname "$NAUT_WEBUI_DB")" "$(dirname "$NAUT_SOCKET")"' \
'mkdir -p "$NAUT_WEBUI_SAVE_PATH" 2>/dev/null || true' \
'# Own the daemon state so it is writable as PUID:PGID. The downloads share' \
'# is left alone — its permissions come from the host / NFS export.' \
'chown -R "$PUID:$PGID" "$NAUT_STATE_DIR" "$(dirname "$NAUT_WEBUI_DB")" "$(dirname "$NAUT_SOCKET")" 2>/dev/null || true' \
'[ -n "$NAUT_SCRIPT" ] && set -- --script "$NAUT_SCRIPT" "$@"' \
'exec gosu "$PUID:$PGID" /app/nautd --socket "$NAUT_SOCKET" --plugin /app/naut_webui.so --state-dir "$NAUT_STATE_DIR" "$@"' \
> /usr/local/bin/entrypoint.sh \
&& chmod +x /usr/local/bin/entrypoint.sh
# Mountpoints (ownership is fixed at runtime by the entrypoint per PUID/PGID).
RUN mkdir -p /data /downloads /run/naut
VOLUME ["/data", "/downloads"]
EXPOSE 8080 6881 6881/udp
# Liveness: ask the daemon to pong over its control socket (no extra tooling).
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD nautctl --socket "$NAUT_SOCKET" ping || exit 1
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]