Files
ironclaw/Dockerfile
Evrard-Nil a55aff980a Add Docker Hub workflow and optimize Dockerfile for size (#1886)
* Add Docker Hub workflow and optimize Dockerfile for size

Switch from Debian bookworm-slim to Alpine for both build and runtime
stages, reducing image size from 162MB to 64MB (60% reduction):

- Alpine + musl: runtime drops from ~88MB to ~11MB, no libssl needed
  (project uses rustls throughout)
- Profile dist: thin LTO for smaller binary (74MB → 53MB)
- panic=abort + codegen-units=1: eliminates unwinding tables and
  enables better whole-program optimization

New docker.yml workflow builds and pushes to nearaidev/ironclaw on
Docker Hub, triggered on push to main, version tags, or manual dispatch.
Uses GitHub Actions cache for Docker layer caching.

Tested locally (macOS) and on gpu07 (Ubuntu): gateway serves HTTP 200,
SSE streams, chat API, memory/tools/settings endpoints all functional,
~700ms startup time.

* Address review feedback

- Fix image size comment (~30MB → remove specific number)
- Add actions:write permission for GHA cache
- Only push semver tag on version tag events (not on every main push)
- Add comment explaining why panic=abort is set via env var

* Make docker workflow release-only via workflow_call

Replace push trigger with workflow_call so release.yml can invoke it,
plus workflow_dispatch for on-demand builds. This prevents every
staging promotion to main from publishing a new image.

Simplified tag logic — always pushes version + latest + sha.

* Pin cargo-chef and wasm-tools versions for reproducible builds
2026-04-01 19:00:38 -07:00

86 lines
2.2 KiB
Docker

# Multi-stage Dockerfile for the IronClaw agent (cloud deployment).
#
# Uses cargo-chef for dependency caching — only rebuilds deps when
# Cargo.toml/Cargo.lock change, not on every source edit.
#
# Alpine-based build + runtime for a minimal image size.
# Statically links against musl; no glibc or libssl needed at runtime.
#
# Build:
# docker build --platform linux/amd64 -t ironclaw:latest .
#
# Run:
# docker run --env-file .env -p 3000:3000 ironclaw:latest
# Stage 1: Install cargo-chef
FROM rust:1.92-alpine AS chef
RUN apk add --no-cache musl-dev pkgconfig cmake gcc g++ make perl \
&& rustup target add wasm32-wasip2 \
&& cargo install cargo-chef@0.1.77 wasm-tools@1.246.1
WORKDIR /app
# Stage 2: Generate the dependency recipe (changes only when Cargo.toml/lock change)
FROM chef AS planner
COPY Cargo.toml Cargo.lock ./
COPY crates/ crates/
COPY build.rs build.rs
COPY src/ src/
COPY tests/ tests/
COPY benches/ benches/
COPY migrations/ migrations/
COPY registry/ registry/
COPY channels-src/ channels-src/
COPY wit/ wit/
COPY providers.json providers.json
RUN cargo chef prepare --recipe-path recipe.json
# Stage 3: Build dependencies (cached unless Cargo.toml/lock change)
FROM chef AS deps
# Docker-only overrides for the dist profile (not in Cargo.toml because
# cargo-dist uses dist for release binaries that need unwinding).
ENV CARGO_PROFILE_DIST_PANIC=abort \
CARGO_PROFILE_DIST_CODEGEN_UNITS=1
COPY --from=planner /app/recipe.json recipe.json
RUN cargo chef cook --profile dist --recipe-path recipe.json
# Stage 4: Build the actual binary (only recompiles ironclaw source)
FROM deps AS builder
COPY Cargo.toml Cargo.lock ./
COPY crates/ crates/
COPY build.rs build.rs
COPY src/ src/
COPY tests/ tests/
COPY benches/ benches/
COPY migrations/ migrations/
COPY registry/ registry/
COPY channels-src/ channels-src/
COPY wit/ wit/
COPY providers.json providers.json
RUN cargo build --profile dist --bin ironclaw
# Stage 5: Minimal runtime
FROM alpine:3.21
RUN apk add --no-cache ca-certificates
COPY --from=builder /app/target/dist/ironclaw /usr/local/bin/ironclaw
COPY --from=builder /app/migrations /app/migrations
# Non-root user
RUN adduser -D -u 1000 ironclaw
USER ironclaw
EXPOSE 3000
ENV RUST_LOG=ironclaw=info
ENTRYPOINT ["ironclaw"]