Dockerfile
Rama main · réplica de lectura del repositorio original.
# nzbfast - multi-stage build → slim runtime image.
# Ships the daemon (`nzbfast serve`) with unrar available for the
# compressed-post fallback paths.
# Base images are pinned by digest (Scorecard Pinned-Dependencies): a tag
# is mutable, so `rust:1-bookworm` is not a reproducible input. The digest
# is the multi-arch manifest LIST, not a per-platform manifest, so the
# arm64 and amd64 builds both still resolve. Dependabot's `docker`
# ecosystem moves these forward weekly - if you unpin one, drop its
# ecosystem entry too, or the pin silently rots.
FROM rust:1-bookworm@sha256:82150a52ec202c1b14d7817e14516c392bb7f5cfebd88f1ed531cb37ebd39922 AS build
WORKDIR /src
# Issue #38: a wedged daemon in the official image could not be given a
# usable backtrace - the release profile strips symbols and the strip
# below strips again, so `gdb -p` resolved 1010 frames of `?? ()`.
# Build with symbols + line tables instead (env overrides the profile's
# `strip = "symbols"`), then split them into a debuglink sidecar the
# runtime stage ships at gdb's standard search path. The binary on PATH
# stays stripped. Set BEFORE the cache-warming stub build too, or the
# profile hash changes and the dependency cache is thrown away.
ENV CARGO_PROFILE_RELEASE_STRIP=none \
CARGO_PROFILE_RELEASE_DEBUG=line-tables-only
# Cache dependency compilation: copy manifests, build a stub, then the source.
COPY Cargo.toml Cargo.lock ./
COPY crates/nzbkit/Cargo.toml crates/nzbkit/
COPY crates/nzbfast/Cargo.toml crates/nzbfast/
RUN mkdir -p crates/nzbkit/src crates/nzbfast/src \
&& echo 'fn main(){}' > crates/nzbfast/src/main.rs \
&& echo '' > crates/nzbkit/src/lib.rs \
&& cargo build --release -p nzbfast 2>/dev/null || true
# Real sources. vendor/ carries the rars/tiny_http/lzma-rust2 forks;
# rapidyenc's C++ for the build.rs FFI moved UNDER crates/ on 20 Sep 2026
# (crates/nzbkit-base/vendor/rapidyenc, TODO 84 blocker 2) so it arrives
# with the `COPY crates/` below - which is BEFORE the real build, so the
# ordering here still holds. web/ + docs/MANUAL.html are
# include_str!-embedded by serve.rs.
COPY vendor/ vendor/
COPY web/ web/
# The whole docs tree: MANUAL.html plus docs/i18n/MANUAL.<lang>.html -
# all include_str!-embedded by serve.rs (the i18n manuals broke the
# image build when only MANUAL.html was copied).
COPY docs/ docs/
COPY crates/ crates/
RUN touch crates/nzbkit/src/lib.rs crates/nzbfast/src/main.rs \
&& cargo build --release -p nzbfast \
&& objcopy --only-keep-debug target/release/nzbfast target/release/nzbfast.debug \
&& objcopy --strip-all --add-gnu-debuglink=target/release/nzbfast.debug \
target/release/nzbfast
FROM debian:bookworm-slim@sha256:88200866dfff7ea7f5cbcb6ec7c8a701889efe6fe859fe64d6990e4b07ea4171
# unrar (non-free) is the real unrar, and unrar-free chokes on too many
# real-world RAR sets. This line used to say it "matches the real unrar
# the desktop bundles embed"; they embed nothing. packaging/
# INSTALLER-SPEC.md item 5 is explicit - "no unrar or par2 binary
# shipped at all" - so this image is the ONLY place the external unrar
# is present by default, and everywhere else it is a tool the operator
# installed. Corrected 2 Sep 2026.
#
# NO p7zip-full. Both images installed it and NOTHING invoked it: 7z has
# been native since the sevenz-rust2 integration and the only
# `tools::resolve` callers ask for unrar and par2. Dropped 2 Sep 2026 as
# pure attack-surface removal (TODO 314, appendix A of
# research/SANDBOX-SCOPING-2026-08.md). Do not put it back to "support
# 7z" - that would be adding a C++ parser for a format we already read
# in safe Rust.
RUN sed -i 's/Components: main/Components: main non-free non-free-firmware/' \
/etc/apt/sources.list.d/debian.sources \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
unrar ca-certificates tini curl gosu \
&& rm -rf /var/lib/apt/lists/*
COPY --from=build /src/target/release/nzbfast /usr/local/bin/nzbfast
# The symbol sidecar, where gdb's debuglink search looks for it
# (/usr/lib/debug + the binary's directory). `apt-get install gdb` in a
# wedged container then gives named, line-numbered frames from
# `gdb -p $(pidof nzbfast) -batch -ex 'thread apply all bt'`.
COPY --from=build /src/target/release/nzbfast.debug /usr/lib/debug/usr/local/bin/nzbfast.debug
LABEL org.opencontainers.image.title="nzbfast" \
org.opencontainers.image.source="https://github.com/nzbfast/nzbfast" \
org.opencontainers.image.url="https://nzbfast.github.io/nzbfast/" \
org.opencontainers.image.vendor="nzbfast"
# Config + data mount points (compose/NAS bind them). WORKDIR /config so
# relative state (index.db) lands on the persisted config volume.
ENV NZBFAST_CONFIG=/config/config.json \
NZBFAST_PORT=6789 \
NZBFAST_OUT=/downloads \
NZBFAST_WATCH=/watch
RUN mkdir -p /config /downloads /watch /incomplete
WORKDIR /config
VOLUME ["/config", "/downloads", "/watch"]
EXPOSE 6789
COPY packaging/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
CMD curl -fsS "http://127.0.0.1:${NZBFAST_PORT:-6789}/api?mode=version&output=json" >/dev/null || exit 1
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]