Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions .github/workflows/build-latest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
11 changes: 10 additions & 1 deletion .github/workflows/build-v173.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
35 changes: 29 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down
26 changes: 20 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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" && \
Expand All @@ -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
Expand Down
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
Loading