diff --git a/.github/workflows/build-latest.yml b/.github/workflows/build-latest.yml index 56e9b53..3232d9c 100644 --- a/.github/workflows/build-latest.yml +++ b/.github/workflows/build-latest.yml @@ -6,15 +6,21 @@ on: pull_request: branches: [ main ] schedule: - - cron: '30 6 * * 1' # Every Monday at 06:30 UTC + # Monthly, 1st at 06:30 UTC. All build inputs are pinned/frozen upstream + # (minio/minio is archived, console + mds are pinned refs, mc is frozen), + # so the only thing a rebuild picks up is Alpine base-image security patches. + - cron: '30 6 1 * *' workflow_dispatch: +concurrency: + group: build-latest-${{ github.ref }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest permissions: contents: read - packages: write timeout-minutes: 240 steps: @@ -52,5 +58,9 @@ jobs: MINIO_VERSION=latest CONSOLE_VERSION=v1.7.6 MDS_COMMIT=027fb7f9834e2bd6daba7f62a9d04d9a3606fbe1 + # Registry cache, not type=gha: GitHub evicts unused caches after 7 days, + # which would leave every monthly run cold. This persists in Docker Hub. + cache-from: type=registry,ref=firstfinger/minio:buildcache-latest + cache-to: ${{ github.event_name != 'pull_request' && 'type=registry,ref=firstfinger/minio:buildcache-latest,mode=max' || '' }} provenance: false sbom: false diff --git a/.github/workflows/build-v173.yml b/.github/workflows/build-v173.yml index dbf7537..493774e 100644 --- a/.github/workflows/build-v173.yml +++ b/.github/workflows/build-v173.yml @@ -3,14 +3,21 @@ name: Build 1.7.3 on: push: branches: [ main ] + schedule: + # Monthly, 2nd at 06:30 UTC - staggered a day after Build Latest so the two + # multi-arch QEMU builds do not contend for the same runner window. + - cron: '30 6 2 * *' workflow_dispatch: +concurrency: + group: build-v173-${{ github.ref }} + cancel-in-progress: true + jobs: build: runs-on: ubuntu-latest permissions: contents: read - packages: write timeout-minutes: 240 steps: @@ -47,5 +54,7 @@ jobs: MINIO_VERSION=latest CONSOLE_VERSION=v1.7.3 MDS_COMMIT=3025ce83976c286fb1e9c55095336d75ad82d8e6 + cache-from: type=registry,ref=firstfinger/minio:buildcache-1.7.3 + cache-to: type=registry,ref=firstfinger/minio:buildcache-1.7.3,mode=max provenance: false sbom: false diff --git a/CLAUDE.md b/CLAUDE.md index 03e973b..a112c7e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,6 +6,8 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co A Docker project that builds a single, self-contained MinIO image from source. It bundles the MinIO server, the Admin Console (UI), and the MinIO client (`mc`) into one image. Pre-built images are published to Docker Hub as `firstfinger/minio`. +**Why it exists:** MinIO stopped shipping a usable community distribution. `minio/minio` is archived on GitHub (read-only; last code change 2025-10-24, last release `RELEASE.2025-10-15T17-29-55Z`), the full-featured web console was stripped out of the community server, and `minio/object-browser` was deleted outright. This repo rebuilds the last open-source server from source and pairs it with a community-maintained console fork, so the resulting image keeps admin features (notably Site Replication) that upstream removed. + ## Common Commands ### Build the Docker image locally @@ -19,9 +21,23 @@ docker build --platform linux/amd64 -t firstfinger/minio:local-amd64 . docker build --platform linux/arm64 -t firstfinger/minio:local-arm64 . # Build with a specific MinIO version -docker build --build-arg MINIO_VERSION=RELEASE.2024-01-01T00-00-00Z -t firstfinger/minio:local . +docker build --build-arg MINIO_VERSION=RELEASE.2025-10-15T17-29-55Z -t firstfinger/minio:local . + +# Build with a specific console version (e.g. the Site Replication build) +docker build \ + --build-arg CONSOLE_VERSION=v1.7.3 \ + --build-arg MDS_COMMIT=3025ce83976c286fb1e9c55095336d75ad82d8e6 \ + -t firstfinger/minio:local-1.7.3 . ``` +### Build arguments + +| Arg | Default | Purpose | +|-----|---------|---------| +| `MINIO_VERSION` | `latest` | Git ref of `minio/minio` to build. `latest` = shallow clone of master. | +| `CONSOLE_VERSION` | `v1.7.6` | Tag of the `Harsh-2002/MinIO-Object-Browser` fork to build the console from. | +| `MDS_COMMIT` | `027fb7f…fbe1` | Commit of the `Harsh-2002/mds` fork, side-loaded as the console's design-system dependency. | + ### Run with Docker Compose ```bash @@ -60,9 +76,9 @@ docker run -d \ The `Dockerfile` has four stages: -1. **`console-ui-builder`** — Node.js/Yarn build of the Admin Console frontend from `minio/object-browser` v1.7.3. +1. **`console-ui-builder`** — Node.js/Yarn build of the Admin Console frontend from the `Harsh-2002/MinIO-Object-Browser` fork at `CONSOLE_VERSION` (upstream `minio/object-browser` is deleted). It also clones the `Harsh-2002/mds` fork at `MDS_COMMIT` and rewrites `web-app/package.json` with `jq` so the `mds` design-system dependency resolves to that local checkout instead of npm. 2. **`console-builder`** — Go build of the Admin Console binary (`console`), embedding the UI assets from stage 1. -3. **`server-builder`** — Go build of `minio` server from source. Accepts `MINIO_VERSION` build arg (defaults to `latest`). +3. **`server-builder`** — Go build of `minio` server from source (shallow clone; `minio/minio` is archived so history is never needed). Accepts `MINIO_VERSION` build arg (defaults to `latest`). Also downloads `mc` from `dl.min.io` and verifies it with minisign — `dl.min.io` 302-redirects to GitHub Releases, so those `curl` calls **must** keep `-L`. 4. **Final runtime** — Alpine-based image that combines all three binaries, plus a downloaded `mc` binary (verified with minisign). Runs as root, matching the official MinIO image behaviour. ### `start.sh` — Entrypoint Logic @@ -75,11 +91,18 @@ The script is the container entrypoint and orchestrates the startup sequence: 4. Starts the `console` binary after the server is healthy. 5. Monitors both processes; exits if either dies. -### CI/CD Workflow (`.github/workflows/docker-build-push.yml`) +### CI/CD Workflows + +Two near-identical workflows, each a single `build` job that uses QEMU to cross-compile arm64 on the amd64 runner and pushes one multi-arch manifest. Timeout is 240 minutes to accommodate the arm64 QEMU build time. + +| Workflow | Tag | Console | Triggers | +|----------|-----|---------|----------| +| `build-latest.yml` | `firstfinger/minio:latest` | `v1.7.6` | push/PR to `main`, monthly (1st, 06:30 UTC), dispatch | +| `build-v173.yml` | `firstfinger/minio:1.7.3` | `v1.7.3` (Site Replication) | push to `main`, monthly (2nd, 06:30 UTC), dispatch | -Single `build` job. Uses QEMU to cross-compile arm64 on the amd64 runner, then pushes a single multi-arch manifest as `firstfinger/minio:latest`. Timeout is 240 minutes to accommodate the arm64 QEMU build time. +**Why monthly, not weekly or daily:** every build input is frozen upstream — `minio/minio` is archived, the console and `mds` refs are pinned, and `mc` on `dl.min.io` has not moved since `RELEASE.2025-08-13`. The only thing a scheduled rebuild can pick up is an Alpine base-image security patch. Rebuilding more often than monthly produces an identical image. -Triggers: push/PR to `main`, daily schedule (06:30 UTC), and manual dispatch. +Both use a **registry** build cache (`firstfinger/minio:buildcache-*`) rather than `type=gha`, because GitHub evicts unused caches after 7 days, which would leave every monthly run cold. ## TLS Configuration diff --git a/Dockerfile b/Dockerfile index d1bc588..9beedd5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -61,14 +61,25 @@ RUN apk add --no-cache ca-certificates git make curl bash && \ go install aead.dev/minisign/cmd/minisign@v0.2.1 # Clone and build server -RUN git clone https://github.com/minio/minio.git . && \ - if [ "$MINIO_VERSION" != "latest" ]; then \ +# minio/minio is archived upstream (read-only; last code change 2025-10-24), so a +# shallow clone of the exact ref is all that is ever needed. +RUN if [ "$MINIO_VERSION" != "latest" ]; then \ echo "Checking out version: $MINIO_VERSION" && \ - git checkout ${MINIO_VERSION}; \ + git clone --depth 1 --branch ${MINIO_VERSION} https://github.com/minio/minio.git . ; \ else \ - echo "Building from latest master"; \ + echo "Building from latest master" && \ + git clone --depth 1 https://github.com/minio/minio.git . ; \ fi +# Pre-fetch modules with retries. proxy.golang.org intermittently drops an HTTP/2 +# stream mid-download; without a retry one flaky zip kills the whole multi-arch build. +RUN n=1; while [ $n -le 5 ]; do \ + go mod download && break; \ + echo "go mod download failed (attempt $n/5), retrying in 10s"; \ + n=$((n+1)); sleep 10; \ + done; \ + [ $n -le 5 ] || { echo "go mod download failed after 5 attempts"; exit 1; } + # Build server binary RUN COMMIT_ID=$(git rev-parse --short HEAD) && \ echo "Building version: $MINIO_VERSION commit: $COMMIT_ID" && \ @@ -78,8 +89,11 @@ RUN COMMIT_ID=$(git rev-parse --short HEAD) && \ /usr/bin/minio --version # Download and verify client binary - use TARGETARCH instead of BUILDARCH -RUN curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc -o /usr/bin/mc && \ - curl -s -q https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc.minisig -o /usr/bin/mc.minisig && \ +# dl.min.io 302-redirects to GitHub Releases, so -L is required; without it curl saves +# the HTML redirect stub and minisign rejects it. -f fails loudly on any HTTP error +# instead of silently writing an error page over the binary. +RUN curl -fsSL https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc -o /usr/bin/mc && \ + curl -fsSL https://dl.min.io/client/mc/release/linux-${TARGETARCH}/mc.minisig -o /usr/bin/mc.minisig && \ chmod +x /usr/bin/mc && \ /go/bin/minisign -Vqm /usr/bin/mc -x /usr/bin/mc.minisig -P RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav && \ /usr/bin/mc --version diff --git a/README.md b/README.md index 95f3fcc..a8e29f7 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,31 @@ A production-ready Docker image that combines the latest MinIO server with the f - **MinIO Admin Console** — Full-featured web UI including Site Replication - **MinIO Client (mc)** — Command-line tools, minisign-verified +## Related project — Cairn + +This image exists because MinIO closed down its community edition. [**Cairn**](https://github.com/Harsh-2002/Cairn) is my answer to the same problem, built from scratch: a self-hosted, S3-compatible object store written in **Rust**, Apache-2.0 licensed, with the console built into the binary. + +- **One binary, no moving parts.** Object data as plain files on a normal filesystem, metadata in embedded SQLite — no external database, no clustering layer, no separate storage daemon. +- **The console ships inside it** — bucket browser with in-place preview (images, video, PDF, Markdown, JSON, CSV), a validated bucket-policy editor, scoped short-lived credentials, and metrics. +- **S3 API** — versioning, multipart, object lock, lifecycle, tagging, CORS, presigned URLs, SigV4. The `aws` CLI and the standard AWS SDKs work against it unmodified. +- Multi-arch image at `ghcr.io/harsh-2002/cairn`, plus static amd64/arm64 binaries with a `SHA256SUMS` manifest. +- One-shot import to migrate buckets and objects straight from an existing MinIO node. + +> **Cairn is under active development and not production-ready yet.** Good for a homelab, a VPS, or a small node you can afford to experiment on. Issues and feedback are very welcome. + +[Project site and screenshots](https://harsh-2002.github.io/Cairn/) · [Repository](https://github.com/Harsh-2002/Cairn) + ## Image Tags | Tag | Console Version | When Updated | |-----|----------------|--------------| -| `latest` | v1.7.6 | Every push to main + weekly (Monday) | -| `1.7.3` | v1.7.3 (Site Replication) | Every push to main + manual dispatch | +| `latest` | v1.7.6 | Every push to main + monthly (1st) | +| `1.7.3` | v1.7.3 (Site Replication) | Every push to main + monthly (2nd) | + +> Rebuilds are monthly rather than weekly because every source input is frozen upstream — +> `minio/minio` is archived, the console and `mds` refs are pinned, and `mc` has not been +> republished since `RELEASE.2025-08-13`. A scheduled rebuild exists to pick up Alpine +> base-image security patches, nothing else. Both tags are multi-arch OCI manifests — Docker automatically selects the right binary for your platform (`linux/amd64` or `linux/arm64`). No need to specify an architecture tag.