loudmetric / Dockerfile
abc1181's picture
fix: redirects pointed at 0.0.0.0 instead of the Space hostname
a728f68
Raw History Blame Contribute Delete
5.18 kB
# LoudMetric on Hugging Face Spaces.
#
# Two processes in one container: Postgres and the Next.js app. Spaces passes
# `--port 7860`, so the app listens there while Postgres stays on a unix socket
# inside the container.
#
# Persistence is a `pg_dump` in a Dataset repo, not a live PGDATA copy. The
# runtime disk is ephemeral and Hugging Face has retired persistent storage, so a
# dump on a schedule is the supported way to keep data. It also happens to be the
# only safe option: PGDATA needs fsync semantics, /dev/shm and background
# processes, and round-tripping a live cluster through git and LFS corrupts it.
#
# The honest cost of this, stated in the UI as well as here: anything written in
# the last SYNC_INTERVAL minutes is lost if the Space restarts. It is a demo and
# a collector, not a production database.
FROM node:22-bookworm-slim
# postgresql-15 for the server; postgresql-client-15 for pg_dump/pg_restore, which
# run on a different schedule than the server itself.
RUN apt-get update && apt-get install -y --no-install-recommends \
postgresql-15 postgresql-client-15 ca-certificates curl \
&& rm -rf /var/lib/apt/lists/*
# NODE_ENV is deliberately NOT set here.
#
# This was the cause of a BUILD_ERROR on the Space that took three rebuilds to
# find. Setting NODE_ENV=production above `npm ci` makes npm skip
# devDependencies, so typescript was never installed, and `next build` then
# failed with a wall of "Module not found: Can't resolve '@/lib/db'" — an error
# that looks like a missing source file or a bad path alias and is neither.
# The tell was npm reporting 36 packages where a full install is 47.
#
# Reproduced locally by running the same two commands in that order: identical
# errors, and `npm ci --include=dev` fixes it.
#
# NODE_ENV is set after the build, where it actually belongs: it is a runtime
# concern, and setting it early only breaks the build.
ENV NEXT_TELEMETRY_DISABLED=1 \
PGDATA=/var/lib/postgresql/data \
DATABASE_URL=postgres://loudmetric:loudmetric@localhost:5432/loudmetric \
PORT=7860 \
HOSTNAME=0.0.0.0 \
SYNC_INTERVAL=900 \
HF_DATASET_ID=""
WORKDIR /app
# Python is only here for huggingface_hub, which drives the dataset sync.
RUN apt-get update && apt-get install -y --no-install-recommends python3 python3-pip \
&& rm -rf /var/lib/apt/lists/* \
&& pip3 install --no-cache-dir --break-system-packages "huggingface_hub>=0.24"
# Dependencies first, so a source-only change does not reinstall the world.
#
# Full install, not --omit=dev: `next build` needs typescript and the standalone
# finalize step runs a script through node, and db/seed.ts needs tsx. Installing
# only runtime deps and then building was the first version of this file, and it
# fails at the build step with a missing-compiler error.
COPY package.json package-lock.json* ./
# --include=dev as well as the un-early NODE_ENV, because one guard against a
# failure mode this subtle is not enough. Prune removes them again further down.
RUN npm ci --include=dev --registry=https://registry.npmjs.org/
COPY . .
RUN npm run build
# Drop the toolchain now that the build is done.
#
# Two things fixed here, both found by an actual BUILD_ERROR on the Space:
#
# --registry is repeated deliberately. `npm ci` above is pinned, but prune
# resolves its own tree and would otherwise hit the default registry — the one
# that serves 403s and, earlier in this project's life, a truncated native
# binary. It failed the whole Space build at this step.
#
# `|| true` because pruning is an image-size optimisation, never a
# correctness requirement. A network hiccup in an optional step should cost a
# few hundred megabytes, not the deployment.
RUN npm prune --omit=dev --registry=https://registry.npmjs.org/ || echo "prune skipped (non-fatal)"
# Runtime only, after the build. See the note at the top of this file.
ENV NODE_ENV=production
# Postgres refuses to run as root, so it gets its own account with its own
# home. The app still runs as root, which is not great practice but matches what
# Spaces expects when the container must bind 7860.
RUN useradd -m -s /bin/bash postgres || true \
&& mkdir -p /var/lib/postgresql/data /var/run/postgresql /dumps \
&& chown -R postgres:postgres /var/lib/postgresql /var/run/postgresql \
&& chmod 777 /dumps
COPY hf/entrypoint.sh /usr/local/bin/loudmetric-entrypoint
COPY hf/sync.py /usr/local/bin/loudmetric-sync
RUN chmod +x /usr/local/bin/loudmetric-entrypoint /usr/local/bin/loudmetric-sync
EXPOSE 7860
# Spaces supplies these; the sync is a no-op without them, which keeps the image
# buildable and testable locally with no Hugging Face account at all.
ARG HF_TOKEN=""
ARG HF_DATASET_ID=""
ENV HF_TOKEN=$HF_TOKEN HF_DATASET_ID=$HF_DATASET_ID
# curl is in the image because of this line specifically. bookworm-slim does not
# ship it, and a HEALTHCHECK whose binary is missing fails forever, which marks
# a perfectly healthy Space as unhealthy.
HEALTHCHECK --interval=60s --timeout=10s --start-period=90s --retries=3 \
CMD curl -fsS http://localhost:7860/api/health || exit 1
ENTRYPOINT ["/usr/local/bin/loudmetric-entrypoint"]