# syntax=docker/dockerfile:1 # The Great Bear — single image running TanStack Start (site) + PocketBase (CMS/API) ARG NODE_VERSION=22 ARG PB_VERSION=0.40.2 # ---------- Stage 1: build the TanStack Start app ---------- FROM node:${NODE_VERSION}-alpine AS build WORKDIR /app COPY app/package.json app/package-lock.json ./ RUN npm ci --no-audit --no-fund COPY app/ ./ RUN npm run build # ---------- Stage 2: runtime ---------- FROM node:${NODE_VERSION}-alpine AS runtime ARG PB_VERSION ARG TARGETARCH ENV NODE_ENV=production \ PORT=3000 \ PB_URL=http://127.0.0.1:8090 WORKDIR /app RUN apk add --no-cache curl unzip # Site server build output COPY --from=build /app/.output ./.output # Content seed (shared with the frontend fallback) + process supervisor COPY app/src/data/content.json ./scripts/content.json COPY scripts/seed.mjs scripts/run-all.mjs ./scripts/ # PocketBase schema migrations COPY pb/migrations ./pb_migrations RUN mkdir -p pb_public # PocketBase binary (static Go binary, runs fine on Alpine) RUN case "${TARGETARCH:-amd64}" in \ arm64) PB_ARCH=arm64 ;; \ *) PB_ARCH=amd64 ;; \ esac \ && curl -fsSL -o /tmp/pb.zip \ "https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_${PB_ARCH}.zip" \ && unzip -o /tmp/pb.zip pocketbase -d /app \ && chmod +x /app/pocketbase \ && rm /tmp/pb.zip \ && mkdir -p /app/pb_data # Runs as root so the Ansible-managed bind mount (/var/lib/greatbear/pb_data) # is writable regardless of host-side ownership. EXPOSE 3000 8090 HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=5 \ CMD curl -fsS http://127.0.0.1:8090/api/health -o /dev/null || exit 1 CMD ["node", "scripts/run-all.mjs"]