# Copyright (c) 2025 Erick Bourgeois, firestoned
# SPDX-License-Identifier: MIT
# This Dockerfile uses cargo-chef for optimal caching of Rust dependencies
# First build: ~5 minutes, subsequent builds: ~30 seconds (if only src/ changes)
#
# To use this Dockerfile:
# docker build -f docker/Dockerfile.chef -t bindcar:latest .
#
# IMPORTANT: This requires cargo-chef to be installed in the base image.
# The rust:1.91.0 image doesn't include it by default, so we install it in a separate stage.
# Stage 1: Prepare cargo-chef
FROM rust:1.96.0 AS chef
RUN cargo install cargo-chef
WORKDIR /workspace
# Stage 2: Generate recipe file (this layer is cached unless Cargo.toml/Cargo.lock change)
FROM chef AS planner
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo chef prepare --recipe-path recipe.json
# Stage 3: Build dependencies (this layer is cached unless recipe changes)
FROM chef AS builder
# Accept version as build argument
ARG VERSION=0.1.0
# Install musl target and cross-compilation tools for static linking
RUN rustup target add aarch64-unknown-linux-musl x86_64-unknown-linux-musl && \
apt-get update && \
apt-get install -y musl-tools && \
rm -rf /var/lib/apt/lists/*
# Determine the musl target based on architecture
RUN ARCH=$(uname -m) && \
if [ "$ARCH" = "aarch64" ]; then \
echo "aarch64-unknown-linux-musl" > /tmp/target; \
else \
echo "x86_64-unknown-linux-musl" > /tmp/target; \
fi
# Copy recipe and build dependencies
COPY --from=planner /workspace/recipe.json recipe.json
RUN TARGET=$(cat /tmp/target) && \
cargo chef cook --release --target $TARGET --recipe-path recipe.json
# Copy source code and build the actual binary
COPY Cargo.toml Cargo.lock ./
COPY src ./src
# Update version in Cargo.toml using the VERSION build argument
RUN sed -i "s/^version = \".*\"/version = \"${VERSION}\"/" Cargo.toml
# Build the actual API server with musl for static linking
RUN TARGET=$(cat /tmp/target) && \
cargo build --release --target $TARGET && \
cp target/$TARGET/release/bindcar target/release/bindcar
# Runtime stage
FROM alpine:3.24
LABEL org.opencontainers.image.source="https://github.com/firestoned/bindcar"
LABEL org.opencontainers.image.description="BIND9 RNDC API Server"
LABEL org.opencontainers.image.licenses="MIT"
# Install runtime dependencies
# Binary is statically linked with musl, so no glibc needed
RUN apk add --no-cache \
ca-certificates \
curl
# Create bindcar user and directories
RUN addgroup -S bindcar && adduser -S -G bindcar bindcar && \
mkdir -p /var/cache/bind && \
chown -R bindcar:bindcar /var/cache/bind
# Copy the built API server from builder
COPY --from=builder /workspace/target/release/bindcar /usr/local/bin/
# Set permissions
RUN chmod +x /usr/local/bin/bindcar
# Run as bindcar user
USER bindcar
# Expose API port
EXPOSE 8080
# Set default environment variables
ENV BIND_ZONE_DIR=/var/cache/bind
ENV API_PORT=8080
ENV RUST_LOG=info
ENV DISABLE_AUTH=false
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/api/v1/health || exit 1
# Start the API server
ENTRYPOINT ["/usr/local/bin/bindcar"]