# CPU budget for the build. The self-hosted runner builds on the Raspberry Pi 5 # (4 cores) alongside live services, so we cap the CPU-heavy tools (the Go # compiler and esbuild — both honour GOMAXPROCS) to leave cores for nginx/NPM # and the other containers. Lower = gentler but slower; raise for a faster box. ARG BUILD_CPUS=2 # ── Stage 1: Build frontend ─────────────────────────────────────────────────── FROM node:20-alpine AS frontend-builder ARG BUILD_CPUS # esbuild (used by Vite) is a Go program and respects GOMAXPROCS. ENV GOMAXPROCS=${BUILD_CPUS} WORKDIR /app/frontend COPY frontend/package*.json ./ RUN npm install COPY frontend/ ./ RUN npm run build # ── Stage 2: Build backend ──────────────────────────────────────────────────── # Go 1.25 required by the OIDC dependency chain (go-oidc/v3, go-jose/v4). FROM golang:1.25-alpine AS backend-builder ARG BUILD_CPUS # Cap the Go compiler's parallelism to keep the Pi responsive during CI. ENV GOMAXPROCS=${BUILD_CPUS} WORKDIR /app/backend # Copy go.mod first for layer caching. go.sum is written by go mod download # when it doesn't exist yet, or verified when it does. COPY backend/go.mod ./ COPY backend/go.su[m] ./ RUN go mod download -x COPY backend/ ./ # CGO disabled — pure Go SQLite (modernc.org/sqlite). -p limits parallel builds. RUN CGO_ENABLED=0 go build -p ${BUILD_CPUS} -ldflags="-s -w" -o /archivum ./cmd/server # ── Stage 3: Runtime image ──────────────────────────────────────────────────── FROM alpine:3.20 # su-exec: lightweight tool to drop privileges (replaces gosu / setpriv). # git: needed for wiki commit operations. # openssh-client: git-sync over ssh:// remotes (git shells out to ssh) RUN apk add --no-cache su-exec git openssh-client # Create the app group and user that the entrypoint will run as. # PUID/PGID can be overridden at runtime via environment variables; # the entrypoint script uses them rather than these build-time values. RUN addgroup -g 1000 archivum \ && adduser -u 1000 -G archivum -s /sbin/nologin -D archivum COPY --from=frontend-builder /app/frontend/dist /srv/archivum/ui COPY --from=backend-builder /archivum /usr/local/bin/archivum COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh # Declare the volume mount points. The entrypoint creates subdirectories # and fixes ownership before the application starts. VOLUME ["/config", "/data"] ENV DOCKER_PATH=/config \ UI_DIR=/srv/archivum/ui \ PUID=1000 \ PGID=1000 # Entrypoint runs as root so it can chown the volumes, then drops to PUID:PGID. EXPOSE 4000 ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]