Dockerfile.selfhost
Rama main · réplica de lectura del repositorio original.
FROM rust:bookworm AS backend-builder
# Build backend
RUN echo "Building backend..."
RUN apt-get update && apt-get install -y \
pkg-config \
libssl-dev \
build-essential \
git \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Caps parallel LLVM instances so this stage fits the image build's memory budget
ENV CARGO_PROFILE_RELEASE_CODEGEN_UNITS=4
COPY backend/Cargo.toml backend/Cargo.lock ./
RUN mkdir src && echo "fn main() {}" > src/main.rs
RUN cargo build --release --locked
RUN rm -rf src target/release/deps/betterlytics* target/release/betterlytics*
COPY backend/src ./src
COPY backend/assets ./assets
RUN cargo build --release --locked
####
# Build dashboard
FROM node:24-slim AS dashboard-builder
# Forces BuildKit to run this stage after the backend build so the two heaviest stages never share peak memory
COPY --from=backend-builder /app/target/release/betterlytics /dev/null
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN echo "Building dashboard..."
ENV NEXT_TELEMETRY_DISABLED=1
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable && corepack prepare pnpm@10.34.5 --activate
RUN apt-get update -y && apt-get install -y openssl && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
COPY dashboard/package.json ./dashboard/
COPY backend/package.json ./backend/
COPY docs/package.json ./docs/
RUN pnpm install --frozen-lockfile --filter dashboard
COPY dashboard/.env.build ./.env
COPY dashboard/prisma ./dashboard/prisma
WORKDIR /app/dashboard
RUN pnpm exec prisma generate
COPY dashboard/ .
RUN pnpm run build
####
# Build initializer
FROM node:24-slim AS initializer-builder
RUN echo "Building initializer..."
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable && corepack prepare pnpm@10.34.5 --activate
WORKDIR /app
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
COPY dashboard/package.json ./dashboard/
COPY backend/package.json ./backend/
COPY docs/package.json ./docs/
RUN pnpm install --frozen-lockfile --filter .
####
# Minify static files
FROM node:24-slim AS static-builder
RUN echo "Minifying static files..."
WORKDIR /app
COPY static/analytics.js static/replay.js static/build.mjs ./
RUN node build.mjs . .
####
# Build Caddy
FROM caddy:2.11.4-builder AS caddy-builder
RUN xcaddy build v2.11.4 \
--with github.com/mholt/caddy-ratelimit@5625512f24f6f59d6f64fb3aafe5eecff0b286db
####
# Runtime image
FROM node:24-slim
RUN echo "Building runtime image..."
RUN apt-get update && apt-get install -y \
supervisor \
openssl \
libssl3 \
ca-certificates \
curl \
gettext-base \
&& rm -rf /var/lib/apt/lists/*
COPY --from=caddy-builder /usr/bin/caddy /usr/bin/caddy
# Caddy runs as root under supervisord; keep certs and the ACME account on the /data volume
ENV XDG_DATA_HOME=/data
ENV XDG_CONFIG_HOME=/config
RUN npm install -g prisma@6.19.3
# The prisma CLI otherwise pings checkpoint.prisma.io on every `prisma migrate deploy` (betterlytics-init runs this image)
ENV CHECKPOINT_DISABLE=1
# Copy backend
RUN echo "Copying backend..."
COPY --from=backend-builder /app/target/release/betterlytics /app/backend/app
COPY --from=backend-builder /app/assets /app/backend/assets
# Copy dashboard
RUN echo "Copying dashboard..."
COPY --from=dashboard-builder /app/dashboard/public /app/dashboard/public
COPY --from=dashboard-builder /app/dashboard/.next/standalone /app/
COPY --from=dashboard-builder /app/dashboard/.next/standalone/worker.js /app/dashboard/worker.js
COPY --from=dashboard-builder /app/dashboard/.next/static /app/dashboard/.next/static
# Copy initializer dependencies
RUN echo "Copying initializer dependencies..."
COPY --from=initializer-builder /app/node_modules /app/initializer/node_modules
# Copy initializer source files
COPY initializer/package.json /app/initializer/
COPY scripts/ /app/initializer/scripts/
COPY migrations/ /app/initializer/migrations/
COPY dashboard/prisma/ /app/initializer/prisma/
# Copy static scripts for Caddy (web-vitals.js ships pre-minified; esbuild would strip its legal banner)
COPY --from=static-builder /app/analytics.min.js /srv/static/analytics.js
COPY --from=static-builder /app/replay.min.js /srv/static/replay.js
COPY static/web-vitals.js /srv/static/web-vitals.js
COPY LICENSE THIRD-PARTY-LICENSES.md /app/
# Startup, proxy and process config. The selfhost compose mounts only /derive.sh.
COPY selfhost/proxy/ /etc/caddy/
COPY selfhost/supervisord.conf /etc/supervisor/conf.d/betterlytics.conf
COPY selfhost/entrypoint.sh selfhost/env.sh selfhost/secrets-env.sh selfhost/healthcheck.sh /app/selfhost/
# Inherited by every container from this image; the selfhost compose disables it for betterlytics-init.
# start-period covers the first HTTPS boot waiting on ACME. --start-interval needs Docker Engine 25.
HEALTHCHECK --interval=30s --timeout=5s --start-period=120s --start-interval=5s --retries=3 \
CMD ["/bin/sh", "/app/selfhost/healthcheck.sh"]
ENTRYPOINT ["/bin/sh", "/app/selfhost/entrypoint.sh"]
CMD ["run"]
# Bake the release version for the dashboard
ARG APP_VERSION=dev
ENV APP_VERSION=$APP_VERSION