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
38 changes: 28 additions & 10 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ ANYTYPE_CLI_VERSION=latest

MONGO_VERSION=7.0.28
REDIS_VERSION=7.2.0-v6
MINIO_VERSION=RELEASE.2025-09-07T16-13-09Z.hotfix.7aa24e772
GARAGE_VERSION=v2.3.0

# Used only by the one-off "migrate" profile (see the Upgrade Guide wiki page).
# MinIO is no longer part of the running stack.
MINIO_LEGACY_VERSION=RELEASE.2025-09-07T16-13-09Z.hotfix.7aa24e772
RCLONE_VERSION=1.71.1


# -----------------------------------------------------------------------------
Expand Down Expand Up @@ -78,7 +83,7 @@ ANY_SYNC_COORDINATOR_DEFAULT_LIMITS_SHARED_SPACES_LIMIT=1000

# -----------------------------------------------------------------------------
# any-sync-filenode
# Handles binary file (blob) storage, backed by MinIO/S3.
# Handles binary file (blob) storage, backed by S3 (Garage).
# -----------------------------------------------------------------------------
ANY_SYNC_FILENODE_HOST=any-sync-filenode
ANY_SYNC_FILENODE_PORT=1005
Expand Down Expand Up @@ -133,15 +138,28 @@ REDIS_MAXMEMORY=256mb


# -----------------------------------------------------------------------------
# MinIO (S3-compatible object storage)
# S3-compatible object storage (Garage)
# Stores files managed by any-sync-filenode.
# -----------------------------------------------------------------------------
AWS_ACCESS_KEY_ID=minio_access_key
AWS_SECRET_ACCESS_KEY=minio_secret_key
MINIO_BUCKET=minio-bucket

MINIO_PORT=9000
MINIO_WEB_PORT=9001
# Replaces MinIO β€” see the Upgrade Guide wiki page before upgrading an existing install.
# -----------------------------------------------------------------------------
AWS_ACCESS_KEY_ID=any_sync_access_key
AWS_SECRET_ACCESS_KEY=any_sync_secret_key

# Internal address of the S3 service. Point it to an external S3-compatible
# storage if you do not want to use the bundled Garage container.
# The port Garage listens on is set in garage/garage.toml.
S3_ENDPOINT=http://garage:3900
S3_BUCKET=any-sync-files
S3_REGION=us-east-1

# Garage cluster RPC secret (64 hex chars). Port 3901 is not published to the host,
# so the default is safe for a single-node setup. Change it if you expose that port:
# openssl rand -hex 32
GARAGE_RPC_SECRET=6b6579206f6e6c792075736564206c6f63616c6c7920627920612073696e676c

# Name of the old MinIO bucket, used by the one-off migration from MinIO.
# Only change this if you customized MINIO_BUCKET before the migration.
S3_LEGACY_BUCKET=minio-bucket


# -----------------------------------------------------------------------------
Expand Down
11 changes: 11 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,19 @@ restart: down start
update: pull down start
upgrade: down clean start

# one-off migration of filenode blobs from MinIO to Garage, see the Upgrade Guide wiki page
migrate:
$(DOCKER_COMPOSE) --profile migrate run --rm s3-migrate
$(DOCKER_COMPOSE) --profile migrate rm -sf minio-legacy

STORAGE_DIR := $(shell grep -m1 '^STORAGE_DIR=' .env 2>/dev/null | cut -d= -f2- | tr -d '"')
STORAGE_DIR := $(if $(STORAGE_DIR),$(STORAGE_DIR),./storage)

cleanEtcStorage:
rm -rf etc/ $(STORAGE_DIR)

# remove the old MinIO data kept as a backup after the migration to Garage
cleanLegacyMinio:
@[ -f $(STORAGE_DIR)/garage/.migrated-from-minio ] || { echo "Error: migration to Garage was not completed β€” refusing to delete $(STORAGE_DIR)/minio. See https://github.com/anyproto/any-sync-dockercompose/wiki/Upgrade-Guide"; exit 1; }
@echo "⚠️ Removing $(STORAGE_DIR)/minio β€” make sure your files are accessible in the Anytype client first."
rm -rf $(STORAGE_DIR)/minio
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ A self-hosted any-sync network, designed for personal use, review, or testing pu
> [!WARNING]
> Before upgrading, read the [Upgrade Guide](../../wiki/Upgrade-Guide).

> [!IMPORTANT]
> MinIO has been replaced by [Garage](https://garagehq.deuxfleurs.fr/) (see
> [#178](https://github.com/anyproto/any-sync-dockercompose/issues/178)).
> Existing installs must run a one-off data migration β€” see the [Upgrade Guide](../../wiki/Upgrade-Guide).
> The stack refuses to start until the migration is done; no data is deleted.

## Requirements

- [Docker](https://docs.docker.com/compose/install/) with Compose plugin v2+
Expand All @@ -22,12 +28,12 @@ The stack runs the following services:
|---|---|---|
| `any-sync-coordinator` | Network coordinator, manages spaces and members | MongoDB |
| `any-sync-node` Γ—3 | Document sync nodes | coordinator |
| `any-sync-filenode` | File storage node | coordinator, MinIO, Redis |
| `any-sync-filenode` | File storage node | coordinator, Garage, Redis |
| `any-sync-consensusnode` | Consensus for conflict resolution | coordinator |
| `netcheck` | Periodic connectivity health monitor | all nodes |
| MongoDB | Coordinator state | β€” |
| Redis | Filenode index | β€” |
| MinIO | S3-compatible object storage for files | β€” |
| Garage | S3-compatible object storage for files | β€” |

Optional service: `anytype-cli` (commented out in `docker-compose.yml`) β€” HTTP/gRPC API server for automation.

Expand Down Expand Up @@ -95,6 +101,8 @@ make upgrade # ⚠️ Full reset: removes containers and volumes, then start
make down # Stop and remove containers (data preserved)
make clean # ⚠️ docker system prune --all --volumes (removes all Docker data)
make cleanEtcStorage # Remove generated ./etc/ configs and ./storage/
make migrate # One-off migration of file storage from MinIO to Garage (see Upgrade Guide)
make cleanLegacyMinio # Remove the old MinIO data kept as a backup after the migration
```

## Documentation
Expand Down
83 changes: 65 additions & 18 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,25 @@ x-anysync-node: &anysync-node
condition: service_started

services:
# refuse to start the stack while data from a previous MinIO install
# has not been migrated to Garage yet (see the Upgrade Guide wiki page)
s3-guard:
restart: "no"
build:
context: .
dockerfile: Dockerfile-any-sync-init
entrypoint: ["bash", "/code/docker-generateconfig/s3-guard.sh"]
volumes:
- ./.env:/code/.env:ro,Z
- ./docker-generateconfig:/code/docker-generateconfig:ro,Z
- "${STORAGE_DIR}:/code/storage:Z"

# generate and process any-sync configs
any-sync-init:
restart: "no"
depends_on:
s3-guard:
condition: service_completed_successfully
build:
context: .
dockerfile: Dockerfile-any-sync-init
Expand Down Expand Up @@ -57,36 +73,67 @@ services:
ports:
- "127.0.0.1:${REDIS_PORT}:${REDIS_PORT}"

minio:
image: "quay.io/minio/minio:${MINIO_VERSION}"
# S3-compatible object storage for any-sync-filenode.
# The bucket and the access key are created automatically on first start.
garage:
image: "dxflrs/garage:${GARAGE_VERSION:?GARAGE_VERSION is not set - your .env is outdated, see https://github.com/anyproto/any-sync-dockercompose/wiki/Upgrade-Guide}"
restart: unless-stopped
command: ["server", "/data", "--console-address", ":${MINIO_WEB_PORT}", "--address", ":${MINIO_PORT}"]
command: ["/garage", "server", "--single-node", "--default-bucket", "--default-access-key"]
environment:
GARAGE_RPC_SECRET: "${GARAGE_RPC_SECRET:?GARAGE_RPC_SECRET is not set - your .env is outdated, see https://github.com/anyproto/any-sync-dockercompose/wiki/Upgrade-Guide}"
GARAGE_DEFAULT_ACCESS_KEY: "${AWS_ACCESS_KEY_ID}"
GARAGE_DEFAULT_SECRET_KEY: "${AWS_SECRET_ACCESS_KEY}"
GARAGE_DEFAULT_BUCKET: "${S3_BUCKET:?S3_BUCKET is not set - your .env is outdated, see https://github.com/anyproto/any-sync-dockercompose/wiki/Upgrade-Guide}"
volumes:
- ./garage/garage.toml:/etc/garage.toml:ro,Z
- "${STORAGE_DIR}/garage:/var/lib/garage:Z"
healthcheck:
test: ["CMD", "/garage", "status"]
interval: 5s
timeout: 10s
retries: 5
start_period: 10s

# --- one-off migration from MinIO to Garage, see the Upgrade Guide -----------
# Not started by "docker compose up"; only by:
# docker compose --profile migrate run --rm s3-migrate

minio-legacy:
profiles: ["migrate"]
image: "quay.io/minio/minio:${MINIO_LEGACY_VERSION}"
restart: "no"
command: ["server", "/data", "--address", ":9000"]
environment:
MINIO_ROOT_USER: "${AWS_ACCESS_KEY_ID}"
MINIO_ROOT_PASSWORD: "${AWS_SECRET_ACCESS_KEY}"
volumes:
- "${STORAGE_DIR}/minio:/data:Z"
healthcheck:
test: bash -c ':> /dev/tcp/127.0.0.1/${MINIO_PORT}' || exit 1
test: bash -c ':> /dev/tcp/127.0.0.1/9000' || exit 1
interval: 5s
timeout: 10s
retries: 3
networks:
default:
aliases:
- "${MINIO_BUCKET}.minio" # <bucket-name>.<endpoint-name>

create-bucket:
s3-migrate:
profiles: ["migrate"]
image: "rclone/rclone:${RCLONE_VERSION}"
restart: "no"
image: quay.io/minio/mc:latest
depends_on:
- minio
entrypoint: >
/bin/sh -c "
until (/usr/bin/mc alias set minio http://minio:${MINIO_PORT} '${AWS_ACCESS_KEY_ID}' '${AWS_SECRET_ACCESS_KEY}') do echo '...waiting...' && sleep 1; done;
/usr/bin/mc mb minio/${MINIO_BUCKET};
exit 0;
"
minio-legacy:
condition: service_healthy
garage:
condition: service_healthy
environment:
AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID}"
AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY}"
S3_BUCKET: "${S3_BUCKET}"
S3_REGION: "${S3_REGION}"
S3_LEGACY_BUCKET: "${S3_LEGACY_BUCKET}"
entrypoint: ["/bin/sh", "/code/docker-generateconfig/migrate-s3.sh"]
volumes:
- ./docker-generateconfig:/code/docker-generateconfig:ro,Z
- "${STORAGE_DIR}:/code/storage:Z"
# ---------------------------------------------------------------------------

any-sync-coordinator_bootstrap:
image: "ghcr.io/anyproto/any-sync-coordinator:${ANY_SYNC_COORDINATOR_VERSION}"
Expand Down Expand Up @@ -124,7 +171,7 @@ services:
depends_on:
redis:
condition: service_healthy
minio:
garage:
condition: service_healthy
any-sync-coordinator:
condition: service_started
Expand Down
8 changes: 4 additions & 4 deletions docker-generateconfig/etc/filenode.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
s3Store:
bucket: %MINIO_BUCKET%
indexBucket: %MINIO_BUCKET%
bucket: %S3_BUCKET%
indexBucket: %S3_BUCKET%
maxThreads: 16
profile: default
region: us-east-1
endpoint: http://minio:%MINIO_PORT%
region: %S3_REGION%
endpoint: %S3_ENDPOINT%
forcePathStyle: true # 'true' for self-hosted S3 Object Storage

redis:
Expand Down
6 changes: 3 additions & 3 deletions docker-generateconfig/etc/node-1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ nodeSync:

s3Store:
enabled: false
region: us-east-1
endpoint: http://minio:%MINIO_PORT%
bucket: %MINIO_BUCKET%
region: %S3_REGION%
endpoint: %S3_ENDPOINT%
bucket: %S3_BUCKET%
keyPrefix: "node_storage/node-1"
forcePathStyle: true # 'true' for self-hosted S3 Object Storage

Expand Down
6 changes: 3 additions & 3 deletions docker-generateconfig/etc/node-2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ nodeSync:

s3Store:
enabled: false
region: us-east-1
endpoint: http://minio:%MINIO_PORT%
bucket: %MINIO_BUCKET%
region: %S3_REGION%
endpoint: %S3_ENDPOINT%
bucket: %S3_BUCKET%
keyPrefix: "node_storage/node-2"
forcePathStyle: true # 'true' for self-hosted S3 Object Storage

Expand Down
6 changes: 3 additions & 3 deletions docker-generateconfig/etc/node-3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ nodeSync:

s3Store:
enabled: false
region: us-east-1
endpoint: http://minio:%MINIO_PORT%
bucket: %MINIO_BUCKET%
region: %S3_REGION%
endpoint: %S3_ENDPOINT%
bucket: %S3_BUCKET%
keyPrefix: "node_storage/node-3"
forcePathStyle: true # 'true' for self-hosted S3 Object Storage

Expand Down
79 changes: 79 additions & 0 deletions docker-generateconfig/migrate-s3.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/bin/sh
# One-off migration of any-sync-filenode blobs from MinIO to Garage.
# Runs inside the rclone image, started by:
# docker compose --profile migrate run --rm s3-migrate
set -e

SRC_BUCKET="${S3_LEGACY_BUCKET:-minio-bucket}"
DST_BUCKET="${S3_BUCKET}"
MARKER="/code/storage/garage/.migrated-from-minio"

# rclone remotes are configured entirely through the environment
export RCLONE_CONFIG_SRC_TYPE=s3
export RCLONE_CONFIG_SRC_PROVIDER=Minio
export RCLONE_CONFIG_SRC_ENDPOINT="http://minio-legacy:9000"
export RCLONE_CONFIG_SRC_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID}"
export RCLONE_CONFIG_SRC_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY}"
export RCLONE_CONFIG_SRC_REGION="${S3_REGION:-us-east-1}"
export RCLONE_CONFIG_SRC_FORCE_PATH_STYLE=true

export RCLONE_CONFIG_DST_TYPE=s3
export RCLONE_CONFIG_DST_PROVIDER=Other
export RCLONE_CONFIG_DST_ENDPOINT="http://garage:3900"
export RCLONE_CONFIG_DST_ACCESS_KEY_ID="${AWS_ACCESS_KEY_ID}"
export RCLONE_CONFIG_DST_SECRET_ACCESS_KEY="${AWS_SECRET_ACCESS_KEY}"
export RCLONE_CONFIG_DST_REGION="${S3_REGION:-us-east-1}"
export RCLONE_CONFIG_DST_FORCE_PATH_STYLE=true

if [ -f "${MARKER}" ]; then
echo "INFO: migration already completed, nothing to do:"
sed 's/^/INFO: /' "${MARKER}"
exit 0
fi

if [ ! -d /code/storage/minio ]; then
echo "INFO: no legacy MinIO data found, nothing to migrate"
exit 0
fi

echo "INFO: copying objects src:${SRC_BUCKET} -> dst:${DST_BUCKET}"
rclone copy "src:${SRC_BUCKET}" "dst:${DST_BUCKET}" \
--checksum --transfers 8 --checkers 8 --stats 5s --stats-one-line

echo "INFO: verifying (checksum comparison, one-way)"
rclone check "src:${SRC_BUCKET}" "dst:${DST_BUCKET}" --one-way --checksum

SRC_COUNT=$(rclone size "src:${SRC_BUCKET}" --json | sed 's/.*"count":\([0-9]*\).*/\1/')
DST_COUNT=$(rclone size "dst:${DST_BUCKET}" --json | sed 's/.*"count":\([0-9]*\).*/\1/')
SRC_BYTES=$(rclone size "src:${SRC_BUCKET}" --json | sed 's/.*"bytes":\([0-9]*\).*/\1/')

if [ "${SRC_COUNT}" -gt "${DST_COUNT}" ]; then
echo "ERROR: object count mismatch: src=${SRC_COUNT} dst=${DST_COUNT}, marker not written" >&2
exit 1
fi

mkdir -p /code/storage/garage
cat > "${MARKER}" <<MARKER_EOF
migrated from MinIO to Garage at $(date -u +%Y-%m-%dT%H:%M:%SZ)
source bucket: ${SRC_BUCKET}, objects: ${SRC_COUNT}, bytes: ${SRC_BYTES}
target bucket: ${DST_BUCKET}, objects: ${DST_COUNT}
MARKER_EOF

cat <<MSG

=================================================================================
Migration finished: ${SRC_COUNT} objects copied and verified.
=================================================================================

Next step:

docker compose up -d (or: make start)

The old MinIO data is kept untouched as a backup. Once you have confirmed that
your files are accessible from the Anytype client, you can remove it:

rm -rf ./storage/minio (or: make cleanLegacyMinio)

=================================================================================

MSG
Loading
Loading