docker: run as non-root, add HEALTHCHECK, document upaas deploy (closes #147)
check / check (push) Failing after 1s

The image had never been run. A trial run (fresh named volume, port 8080,
real targets, no notification endpoints) showed it exited at once: Viper
searches the working directory and, with a YAML config type, also matches an
extension-less file named dnswatcher, so the binary at /app/dnswatcher was
parsed as a config file. The binary now lives in /usr/local/bin and the
working directory is the data dir.

The runtime stage also gains an unprivileged dnswatcher user (uid 10001) that
owns /var/lib/dnswatcher, so a fresh named volume inherits writable ownership,
and a Docker HEALTHCHECK that probes /.well-known/healthcheck with busybox
wget. README gains a "Deploying with upaas" section.

Model: opus-4-8
This commit is contained in:
2026-09-21 07:56:04 +00:00
parent b351a2350c
commit df80bc1fb6
3 changed files with 53 additions and 6 deletions
+22 -6
View File
@@ -41,15 +41,31 @@ FROM alpine@sha256:c3f8e73fdb79deaebaa2037150150191b9dcbfba68b4a46d70103204c53f4
RUN apk add --no-cache ca-certificates tzdata
WORKDIR /app
# The binary lives in /usr/local/bin, not the working directory: config
# loading searches the working directory, and with a YAML config type Viper
# also matches an extension-less file named "dnswatcher" there, so a binary
# named "dnswatcher" in that directory would be parsed as a config file.
COPY --from=builder /src/bin/dnswatcher /usr/local/bin/dnswatcher
COPY --from=builder /src/bin/dnswatcher /app/dnswatcher
# Create data directory
RUN mkdir -p /var/lib/dnswatcher
# Run as an unprivileged user. The data directory is owned by that user so
# writes succeed both on a bind mount and when Docker seeds a fresh named
# volume from the image (a fresh volume inherits this directory's ownership).
RUN addgroup -S dnswatcher \
&& adduser -S -G dnswatcher -u 10001 dnswatcher \
&& mkdir -p /var/lib/dnswatcher \
&& chown dnswatcher:dnswatcher /var/lib/dnswatcher
ENV DNSWATCHER_DATA_DIR=/var/lib/dnswatcher
WORKDIR /var/lib/dnswatcher
USER dnswatcher
EXPOSE 8080
ENTRYPOINT ["/app/dnswatcher"]
# busybox wget (already in alpine) probes the health endpoint. PORT defaults
# to 8080 and is honoured if the operator overrides it.
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -q -O /dev/null "http://127.0.0.1:${PORT:-8080}/.well-known/healthcheck" || exit 1
ENTRYPOINT ["/usr/local/bin/dnswatcher"]