# Debian-based (not alpine) — argon2 and sharp ship native bindings that are # more reliably prebuilt for glibc than musl, and image size isn't a priority # for a self-hosted single-instance app. # # Postgres runs inside this same image/container (not a separate one) so a # self-hoster only has one thing to install and manage — appropriate for a # single-user appliance-style app, even though it trades away the usual # one-process-per-container Docker convention. FROM node:22-bookworm-slim AS base # Ties the published image back to this repo, which is what makes it show up # under the repository's Packages on GitHub. LABEL org.opencontainers.image.source="https://github.com/TimmyAmant/marquee" \ org.opencontainers.image.description="Self-hosted media dashboard for Plex/Jellyfin/Sonarr/Radarr, with a native macOS client." \ org.opencontainers.image.licenses="MIT" # Pinned to a specific major version (rather than the bare "postgresql" # package, whose version tracks whatever Debian bookworm currently # defaults to) so a future rebuild never silently ends up with a server # version that can't read data directories from earlier installs — bump # this deliberately, alongside a documented migration path, not by accident. # # gosu is how entrypoint.sh drops from root (which it needs to run Postgres # as the postgres user) to the unprivileged `node` user for the app itself. RUN apt-get update && apt-get install -y --no-install-recommends postgresql-15 gosu \ && rm -rf /var/lib/apt/lists/* WORKDIR /app # Install dependencies separately so this layer is cached across rebuilds # that only change application code. COPY package.json package-lock.json ./ RUN npm ci COPY . . # `next build` imports every route module (including lib/db/client.ts, which # requires DATABASE_URL to be set) to collect page data, but never actually # queries the database during that step — postgres-js connects lazily on # first query. This placeholder only needs to exist, never to be reachable; # the real DATABASE_URL is built at container startup by entrypoint.sh. ENV DATABASE_URL="postgres://build:build@build-time-placeholder:5432/build" # The app runs as `node`, and Next.js writes to .next at runtime (the image # optimizer's and fetch() caches) — so .next is the one part of the app it # owns. Chowned in the same layer as the build so it isn't stored twice. RUN npm run build && chown -R node:node .next ENV NODE_ENV=production EXPOSE 3000 # Settings › Logs keeps a rolling copy of the server's log here # (lib/logs/capture.ts), so a restart doesn't lose it. Mount a folder on it # (Unraid's template and docker-compose.yml do) and it survives the container # being recreated too; without one it lasts as long as the container. ENV MARQUEE_LOG_DIR=/var/log/marquee RUN mkdir -p /var/log/marquee && chown node:node /var/log/marquee COPY entrypoint.sh /app/entrypoint.sh RUN chmod +x /app/entrypoint.sh ENTRYPOINT ["/app/entrypoint.sh"] CMD ["npm", "run", "start"]