# Production Dockerfile for Backend API # Optimized for smaller size and security # Build stage FROM golang:1.23-alpine AS builder WORKDIR /app # Install build dependencies RUN apk add --no-cache git ca-certificates tzdata # Copy go mod files first for better caching COPY go.mod go.sum ./ # Download dependencies (this layer will be cached if go.mod/go.sum don't change) RUN go mod download # Copy source code COPY . . # Build the application with optimizations # - CGO_ENABLED=0: static binary, no C dependencies # - -ldflags="-w -s": strip debug info and symbol table # - -trimpath: remove file system paths from binaries RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \ -a -installsuffix cgo \ -ldflags="-w -s -extldflags '-static'" \ -trimpath \ -o veza-api \ ./cmd/api/main.go # Runtime stage - minimal alpine FROM alpine:3.21 # Install only runtime dependencies RUN apk --no-cache add ca-certificates tzdata && \ # Add wget for health checks apk --no-cache add wget && \ # Clean up apk cache rm -rf /var/cache/apk/* # Create non-root user for security RUN addgroup -g 1001 -S app && \ adduser -S app -u 1001 -G app -h /app -s /bin/sh # Create app directory WORKDIR /app # Copy binary from builder COPY --from=builder --chown=app:app /app/veza-api /app/veza-api # Copy migrations if they exist (optional) # Use RUN with conditional check to handle missing migrations directory gracefully RUN --mount=from=builder,source=/app,target=/tmp/builder \ if [ -d /tmp/builder/migrations ] && [ "$(ls -A /tmp/builder/migrations 2>/dev/null)" ]; then \ cp -r /tmp/builder/migrations /app/migrations && \ chown -R app:app /app/migrations; \ fi # Switch to non-root user USER app # Expose port EXPOSE 8080 # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:8080/api/v1/health || exit 1 # Run the application ENTRYPOINT ["./veza-api"]