# syntax=docker/dockerfile:1
# Stage 1: Builder
# Using latest stable Rust
FROM rust:bookworm AS builder
WORKDIR /build
# Install Node.js for frontend build
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
apt-get install -y nodejs
# Copy manifests and build infrastructure
COPY Cargo.toml Cargo.lock ./
COPY .cargo ./.cargo
COPY build.rs ./
# Pre-build dependencies with a dummy source file.
# This layer is cached until Cargo.toml or Cargo.lock change,
# so dependency compilation (~90% of build time) is skipped on
# source-only changes.
RUN mkdir -p src frontend/dist && \
echo "fn main() {}" > src/main.rs && \
cargo build --release && \
rm -rf src
# Copy frontend source and build it
COPY frontend ./frontend
RUN cd frontend && \
npm ci && \
npm run build && \
echo "Frontend built successfully:" && \
ls -lh dist/
# Accept build arguments for version info AFTER dependency cache layers,
# so that changing commit SHAs don't invalidate the dependency cache.
ARG GIT_COMMIT=unknown
ARG GIT_BRANCH=unknown
ARG GITHUB_SHA
ARG GITHUB_REF_NAME
# Set environment variables for build.rs to use
ENV GIT_COMMIT=${GIT_COMMIT}
ENV GIT_BRANCH=${GIT_BRANCH}
ENV GITHUB_SHA=${GITHUB_SHA}
ENV GITHUB_REF_NAME=${GITHUB_REF_NAME}
# Copy real Rust source and rebuild (only fspulse crate recompiles)
COPY src ./src
RUN cargo build --release
# Strip binary to reduce size
RUN strip target/release/fspulse || true
# Stage 2: Runtime
FROM debian:bookworm-slim
# Install runtime dependencies
RUN apt-get update && apt-get install -y \
ca-certificates \
tini \
curl \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user (uid 1000, gid 1000)
RUN groupadd -r -g 1000 fspulse && useradd -r -u 1000 -g fspulse fspulse
# Create application directory
WORKDIR /app
# Copy binary from builder
COPY --from=builder /build/target/release/fspulse /app/fspulse
# Copy entrypoint script
COPY docker/entrypoint.sh /app/entrypoint.sh
RUN chmod +x /app/entrypoint.sh
# Create data and roots directories
RUN mkdir -p /data /roots && chown -R fspulse:fspulse /data /roots
# Set environment variables (entrypoint.sh will switch to non-root user)
ENV FSPULSE_DATA_DIR=/data \
FSPULSE_SERVER_HOST=0.0.0.0 \
FSPULSE_SERVER_PORT=8080
# Expose web UI port
EXPOSE 8080
# Health check (using HTTP endpoint)
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/health || exit 1
# Volume mount points
VOLUME ["/data", "/roots"]
# Use tini as init system
ENTRYPOINT ["/usr/bin/tini", "--", "/app/entrypoint.sh"]
# Default command
CMD ["serve"]