bindcar 0.7.0

HTTP REST API for managing BIND9 zones via rndc
# 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"]