Files
seaweedfs/docker/README.md
T
Chris Lu a8f763e717 Sign the published Docker images with cosign (#11129)
* ci: composite action that signs and verifies an image with cosign

Keyless, by digest, with a verification pass against the calling workflow's
own identity right after signing. Signatures use the .sig tag layout rather
than the OCI-referrer bundle cosign 3 writes by default, since that is what
the verifiers people run today read. Dependabot is pointed at the action so
the cosign-installer pin keeps moving.

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* docker release: sign every variant on both registries

The merge job signs each variant's multi-arch index on GHCR and Docker Hub
once the tag exists, recursively so the platform images are covered too.
latest re-tags the same manifest and inherits the signature.

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* docker dev: sign the dev image

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* docker latest: sign a latest rebuilt by hand

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* docker release: sign the foundationdb image

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* docker: sign the per-version foundationdb and rocksdb builds

They push to the same repository as the releases, so an admission policy
that verifies chrislusf/seaweedfs would otherwise reject them.

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* docker: document image signature verification

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* ci: pin the actions the signing jobs newly run by commit

These run with registry credentials and the OIDC token that signs under
the repository's identity, so a retargeted tag upstream must not be able
to reach them.

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* docker latest: pass the dispatch tag through env, not the script

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* docker: complete Kyverno policy, digest note, identity scope

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* docker latest: keep the dispatch tag out of the manifest script too

The step predates signing, but the job now holds the OIDC identity.

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* ci: pin every action in the jobs that sign

The jobs that hold the OIDC identity run these with registry credentials,
so a retargeted tag upstream must not reach them.

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* docker release: copy and sign the digest the run created, pin the rest

crane copy and the signature both resolved the tag, which another
publisher could move between the two steps. The index digest is read once,
right after it is created, and the Docker Hub copy and both signatures use
it. The manual latest rebuild gets the same treatment. The actions in these
jobs are pinned to commits, crane to v0.22.0 by checksum, and the sparse
checkout no longer keeps the token.

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* docker latest: the signing job checks out the workflow's own commit

The job only assembles and signs manifests, so nothing there needs the
source_ref checkout; the local signing action now comes from the same
revision as the workflow file that calls it.

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa

* docker release: take the index digest from the create result

imagetools create writes the descriptor it pushed with --metadata-file
(buildx 0.32+, the runners ship 0.36), so the digest no longer comes from
re-resolving the tag even within the same step.

Claude-Session: https://claude.ai/code/session_01A5zMqzaUg1Snur4Yg8xJGa
2026-09-03 11:52:10 -07:00

3.8 KiB

Docker

Compose V2

SeaweedFS now uses the v2 syntax docker compose

If you rely on using Docker Compose as docker-compose (with a hyphen), you can set up Compose V2 to act as a drop-in replacement of the previous docker-compose. Refer to the Installing Compose section for detailed instructions on upgrading.

Confirm your system has docker compose v2 with a version check

$ docker compose version
Docker Compose version v2.10.2

Try it out


wget https://raw.githubusercontent.com/seaweedfs/seaweedfs/master/docker/seaweedfs-compose.yml

docker compose -f seaweedfs-compose.yml -p seaweedfs up

Try latest tip


wget https://raw.githubusercontent.com/seaweedfs/seaweedfs/master/docker/seaweedfs-dev-compose.yml

docker compose -f seaweedfs-dev-compose.yml -p seaweedfs up

Verify an image signature

Every image CI pushes to chrislusf/seaweedfs and ghcr.io/chrislusf/seaweedfs is signed with cosign, keyless, by the GitHub Actions workflow that built it, so there is no key to fetch or pin. The signature is attached to the image digest and covers the multi-arch index and each platform image in it; latest is the release image under another tag and verifies the same way. Images published before September 2026 predate signing.

cosign verify \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com \
  --certificate-identity-regexp '^https://github.com/seaweedfs/seaweedfs/\.github/workflows/container_release_unified\.yml@' \
  chrislusf/seaweedfs:latest

cosign prints the digest it verified. Deploy by that digest, or let an admission controller resolve the tag, so what runs is what was checked.

The identity is https://github.com/seaweedfs/seaweedfs/.github/workflows/<workflow>@<ref>. The ref is refs/tags/<version> for a release and refs/heads/master when a variant was republished by hand. The workflow is container_release_unified.yml for the release images, container_dev.yml for dev, container_latest.yml for a latest rebuilt by hand, container_release_foundationdb.yml for the _large_disk_foundationdb release image, and container_foundationdb_version.yml or container_rocksdb_version.yml for the per-version builds. The regexp above accepts release images only; container_[a-z_]+\.yml@ accepts everything this repository publishes, dev included.

The same check as a Kyverno policy, release images only:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-seaweedfs-images
spec:
  validationFailureAction: Enforce
  webhookTimeoutSeconds: 30
  rules:
    - name: signed-by-the-release-workflow
      match:
        any:
          - resources:
              kinds:
                - Pod
      verifyImages:
        - imageReferences:
            - "docker.io/chrislusf/seaweedfs:*"
            - "ghcr.io/chrislusf/seaweedfs:*"
          attestors:
            - entries:
                - keyless:
                    issuer: https://token.actions.githubusercontent.com
                    subject: https://github.com/seaweedfs/seaweedfs/.github/workflows/container_release_unified.yml@refs/tags/*

Local Development

cd $GOPATH/src/github.com/seaweedfs/seaweedfs/docker
make

S3 cmd

list

s3cmd --no-ssl --host=127.0.0.1:8333 ls s3://

Build and push a multiarch build

Make sure that docker buildx is supported (might be an experimental docker feature)

BUILDER=$(docker buildx create --driver docker-container --use)
docker buildx build --pull --push --platform linux/386,linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 . -t chrislusf/seaweedfs
docker buildx stop $BUILDER

Minio debugging

mc config host add local http://127.0.0.1:9000 some_access_key1 some_secret_key1
mc admin trace --all --verbose local