diff --git a/.agents/skills/rustfs-docs/SKILL.md b/.agents/skills/rustfs-docs/SKILL.md index 25806e04..44dabda9 100644 --- a/.agents/skills/rustfs-docs/SKILL.md +++ b/.agents/skills/rustfs-docs/SKILL.md @@ -12,8 +12,8 @@ description: >- # Writing RustFS documentation This is a **FumaPress** site (Waku + Fumadocs). Pages live in `content/`; each -page's URL mirrors its path (`content/management/bucket/creation.md` → -`/management/bucket/creation`). Navigation is defined by `content/meta.json` +page's URL mirrors its path (`content/administration/data/bucket/creation.md` → +`/administration/data/bucket/creation`). Navigation is defined by `content/meta.json` (root sidebar) and per-folder `meta.json` files. Read `STYLE.md` (repo root) for the full style guide and `AGENTS.md` for repo @@ -93,8 +93,8 @@ Second person ("you"). Official recommendations as "We recommend". No superlatives or promises (fastest, perfect, 100% …). Technical pages carry instructions, not sales copy — solutions/marketing content belongs on the main site (rustfs.com), not in these docs. The docs sidebar has seven sections: -Getting Started · Core Concepts · Installation · Administration · Operations · -Developer · Reference. New pages go into one of these. +Installation · Administration · Security & Compliance · Operations · +Troubleshooting · Developer · Reference. New pages go into one of these. ## 6. Navigation diff --git a/AGENTS.md b/AGENTS.md index b7997898..fdc0aa76 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,7 +8,7 @@ This playbook directs AI agents working in the RustFS documentation repository s - Framework: **FumaPress** (static-site generator powered by Waku + Fumadocs). Content lives in `content/`; site configuration is in `press.config.tsx`, `source.config.ts`, and `waku.config.ts`. - Goal: produce documentation for a distributed object storage product aimed at a global audience, currently English-first with room for other locales. - Navigation: `content/meta.json` (root sidebar: section order, labels, links) and per-folder `content/**/meta.json` (nested group titles/order) define the site structure. New pages must be reflected there immediately. -- Routing: a page's URL mirrors its path under `content/` (e.g. `content/management/bucket/index.md` → `/management/bucket`). `.md` links between pages are resolved automatically; you can write `./sibling.md` or `/absolute/path`. +- Routing: a page's URL mirrors its path under `content/` (e.g. `content/administration/data/bucket/creation.md` → `/administration/data/bucket/creation`). `.md` links between pages are resolved automatically; you can write `./sibling.md` or `/absolute/path`. ## 2. Core Principles 1. **Accuracy**: Data, APIs, and commands must be reproducible; cite third-party information with a concise source note. diff --git a/STYLE.md b/STYLE.md index 68b5a2cf..e62a3cd4 100644 --- a/STYLE.md +++ b/STYLE.md @@ -9,7 +9,7 @@ This guide defines the writing and formatting conventions for all pages under `c - No superlatives or promises: avoid *fastest*, *best-in-class*, *world-leading*, *100% secure*, *100% compatible*, *perfect*, *military-grade*. - No marketing CTAs ("Contact us immediately", "Buy now") inside technical pages. - Every factual claim must be verifiable against the [rustfs/rustfs](https://github.com/rustfs/rustfs) source or an official release. Never document hypothetical commands, flags, or components. If you are unsure whether a capability exists, remove the claim. -- Do not restate performance numbers on feature pages; link to `/concepts/comparison` instead so figures live in one place. +- Keep performance figures in one verified reference location instead of repeating them across pages. - Use USD (`$`) for any cost examples; never mix currencies. ## Page Skeleton @@ -74,7 +74,7 @@ RUSTFS_VOLUMES="/data" ## Links -- Internal links are root-relative paths without file extension: `/installation/linux/quick-start`, `/concepts/comparison`. +- Internal links are root-relative paths without file extension: `/installation/linux/quick-start`, `/operations/status-check`. - Link text describes the target ("see the cloud-native installation guide"), never "click here". ## Images and Screenshots @@ -89,11 +89,11 @@ RUSTFS_VOLUMES="/data" - Plain Markdown pages use `.md`. - Pages that need JSX components (``, ``, ``, etc.) must use the `.mdx` extension. Do not put JSX in `.md` files. - Mermaid diagrams are supported in fenced ```mermaid blocks. -- **Card icons:** when a landing grid (``) uses icons, use a single consistent set of monochrome line icons from `lucide-react` (registered in `press.config.tsx`), one semantically matched icon per card, applied to **every** card in the grid — never a partial set. Do not use emoji-as-icons and do not mix brand logos (e.g. the Docker whale) with abstract icons. Pass them via `} …>`. +- **Card icons:** use one consistent icon family within each landing grid and apply an icon to every card. Use Lucide for abstract concepts. A platform-selection grid may use brand logos from `react-icons` when every primary card icon is a brand logo. Do not mix brand logos with abstract primary icons or use emoji as icons. ## Product Terminology - The product is **RustFS** (capital R, capital FS) — never "rustFS", "Rustfs", or "RUSTFS". -- RustFS ships as a single binary and an official **Helm chart** for Kubernetes. There is **no Operator, no Tenant CRD, and no KES component** — do not reference them. +- RustFS ships as a single binary and an official **Helm chart** for Kubernetes. The separate official **RustFS Operator** repository provides a pre-release `Tenant` CRD; verify its current release status before documenting it. RustFS has no KES component. - Key management is the **built-in KMS** with `local`, `vault`, and `vault-transit` backends. - Observability is **OpenTelemetry (OTLP)**-based: metrics, logs, and traces export through an OTLP endpoint. diff --git a/content/administration/console/images/rustfs-console-login.png b/content/administration/console/images/rustfs-console-login.png new file mode 100644 index 00000000..6282809d Binary files /dev/null and b/content/administration/console/images/rustfs-console-login.png differ diff --git a/content/administration/console/index.md b/content/administration/console/index.md new file mode 100644 index 00000000..489ef506 --- /dev/null +++ b/content/administration/console/index.md @@ -0,0 +1,69 @@ +--- +title: "Console" +description: "Enable the RustFS Console, connect it to a server, and sign in securely." +--- + +The **RustFS Console** is the web administration interface for RustFS. Use this page to enable the Console, open the login page, and choose the appropriate sign-in method. Detailed procedures for buckets, objects, and identity management are covered in their respective documentation sections. + +## Enable the Console + +The Console is enabled by default and listens on port `9001`, separately from the S3 API on port `9000`. You can set the behavior explicitly with the following environment variables: + +```ini title="/etc/default/rustfs" +RUSTFS_CONSOLE_ENABLE=true +RUSTFS_CONSOLE_ADDRESS=":9001" +``` + +Restart RustFS after changing these values. Set `RUSTFS_CONSOLE_ENABLE=false` when the Console must not run. + +The equivalent command-line options are `--console-enable` and `--console-address`. See the [CLI reference](/reference/cli) and [environment variable reference](/reference/environment-variables) for the complete server configuration. + +## Open the Console + +Open the following address, replacing `` with the RustFS server address: + +```text +http://:9001 +``` + +![RustFS Console login page with key, STS, and OIDC sign-in options](./images/rustfs-console-login.png) + +For a local deployment, use `http://localhost:9001`. Windows and macOS desktop launchers use port `7001` instead. + +If the login page cannot reach the target RustFS service, select **Server Configuration** or open `/config`. Enter the externally reachable RustFS service address and save it after the health check succeeds. **Reset** clears the saved address; **Skip** returns to login without changing it. + +## Log in + +The login methods shown depend on the deployment configuration: + +- **Key Login** uses the access key and secret key configured for the RustFS deployment. This is the standard login method for a local administrator. +- **STS Login** uses temporary Security Token Service (STS) credentials. Use it only when your identity workflow has issued a valid session token. +- **OIDC Login** appears when an OpenID Connect (OIDC) provider is configured. Select the provider and complete authentication with the identity provider. + +After login, the Console opens the first page your account can access. Menus and actions vary by account policy and enabled platform capabilities; a missing menu does not necessarily indicate a Console error. + +If login fails, verify the selected login method, credentials, target server address, and account status before retrying. + +:::warning[Do not expose default credentials] + +RustFS falls back to `rustfsadmin` / `rustfsadmin` when custom credentials are not configured. Use these defaults only for a throwaway local test. Configure a unique access key and a strong secret key before making the Console reachable by other users. + +::: + +## Operational notes + +- Use [TLS](/integration/tls-configured) before exposing the Console outside a trusted network. +- Restrict network access to the Console listener and configure [Console CORS](/administration/cors) only when cross-origin access is required. +- The Console session inherits the permissions of the signed-in identity. Use a least-privilege account for routine work. +- Signing out or an expired session returns you to the login page. Do not store administrator credentials in shared browsers. + +## Management workflows + +- [Create and manage buckets](/administration/data/bucket/creation) +- [Upload and manage objects](/administration/data/object/creation) +- [Manage access keys](/security-compliance/iam/access-token) +- [Configure identity and access management](/security-compliance/iam) + +## Next steps + +Review the [security checklist](/installation/requirement/checklists/security-checklists) before exposing the Console outside a trusted network. For OIDC-based login, continue with the [OIDC configuration guide](/security-compliance/oidc). diff --git a/content/administration/cors/index.md b/content/administration/cors/index.md new file mode 100644 index 00000000..d4e52db4 --- /dev/null +++ b/content/administration/cors/index.md @@ -0,0 +1,50 @@ +--- +title: "CORS Configuration" +description: "Configure allowed browser origins for the RustFS S3 API and Console." +--- + +Cross-Origin Resource Sharing (CORS) controls which browser origins can access the RustFS S3 API and Console. Configure each listener separately, then restart RustFS to apply the environment changes. + +## S3 API origins + +Set `RUSTFS_CORS_ALLOWED_ORIGINS` to a comma-separated list of trusted origins: + +```ini title="/etc/default/rustfs" +RUSTFS_CORS_ALLOWED_ORIGINS="https://app.example.com,https://admin.example.com" +``` + +When this variable is unset or empty, the S3 endpoint does not add generic CORS response headers. A list of explicit origins allows credentialed browser requests from matching origins. + +You can set the value to `*` to allow requests from any origin. Wildcard mode does not allow browser credentials. + +:::warning[Use explicit origins in production] + +Use a comma-separated allowlist for applications that send credentials. Reserve `*` for public resources that do not require credentialed browser requests. + +::: + +## Console origins + +The Console uses a separate variable: + +```ini title="/etc/default/rustfs" +RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS="https://admin.example.com" +``` + +Use a comma-separated list when more than one browser origin must access the Console. Set `*` only when any origin must be allowed. + +## Verify the response + +Send a request with an `Origin` header and inspect the CORS response headers: + +```bash +curl -i \ + -H "Origin: https://app.example.com" \ + http://localhost:9000/ +``` + +Confirm that `Access-Control-Allow-Origin` contains the expected origin. Repeat the check with an unlisted origin and confirm that it is not allowed. + +## Next steps + +See the [environment variable reference](/reference/environment-variables#cors) for the verified defaults and configuration formats. \ No newline at end of file diff --git a/content/management/bucket/creation.md b/content/administration/data/bucket/creation.md similarity index 62% rename from content/management/bucket/creation.md rename to content/administration/data/bucket/creation.md index 5efe2f15..46804ffc 100644 --- a/content/management/bucket/creation.md +++ b/content/administration/data/bucket/creation.md @@ -1,15 +1,14 @@ --- title: "RustFS Bucket Creation" -description: "Create buckets using the RustFS UI, MinIO Client, or API." +description: "Create buckets using the RustFS UI, rc, or the S3 API." --- -This guide explains how to create buckets using the RustFS UI, `mc` (MinIO Client), or API. +This guide explains how to create buckets using the RustFS UI, `rc`, or the S3 API. -## Creating Buckets +## Requirements -Prerequisites: - -- A running RustFS instance (see [Installation Guide](../../installation/index.md)). +- A running RustFS instance (see [Installation Guide](../../../installation/index.md)). +- [`rc`](/operations/rc) installed and configured with an alias for the command-line workflow. ## Using the RustFS UI @@ -19,22 +18,22 @@ Prerequisites: ![bucket creation](images/bucket-creation-by-ui.png) -### Using `mc` +## Using `rc` -> See the [`mc` Usage Guide](../../developer/mc.md) for installation and configuration. +See the [`rc` guide](/operations/rc) for installation and alias configuration. Create a bucket: ```bash -# create rustfs bucket -mc mb rustfs/bucket-creation-by-mc -Bucket created successfully `rustfs/bucket-creation-by-mc`. +rc bucket create rustfs/my-bucket +rc bucket list rustfs/ +``` -# confirm bucket creation -mc ls rustfs/bucket-creation-by-mc +```text +✓ Bucket 'rustfs/my-bucket' created successfully. ``` -### Using the API +## Using the API Create a bucket via API: diff --git a/content/management/bucket/deletion.md b/content/administration/data/bucket/deletion.md similarity index 68% rename from content/management/bucket/deletion.md rename to content/administration/data/bucket/deletion.md index 90eb7a23..b2234a81 100644 --- a/content/management/bucket/deletion.md +++ b/content/administration/data/bucket/deletion.md @@ -1,9 +1,14 @@ --- title: "RustFS Bucket Deletion" -description: "Delete buckets using the RustFS UI, MinIO Client, or API." +description: "Delete buckets using the RustFS UI, rc, or the S3 API." --- -This guide explains how to delete buckets using the RustFS UI, `mc` (MinIO Client), or API. +This guide explains how to delete buckets using the RustFS UI, `rc`, or the S3 API. + +## Requirements + +- Install and configure [`rc`](/operations/rc) before using the command-line workflow. +- Empty the target bucket before deleting it, or use `--force` only after reviewing the objects that will be removed. **Warning**: Deleting a bucket is irreversible and may break applications relying on it. Ensure you have backed up any necessary data before proceeding. @@ -16,20 +21,18 @@ This guide explains how to delete buckets using the RustFS UI, `mc` (MinIO Clien ![bucket deletion](images/bucket-deletion-on-ui.png) -## Using `mc` +## Using `rc` -See the [`mc` Usage Guide](../../developer/mc.md) for installation and configuration. +See the [`rc` guide](/operations/rc) for installation and alias configuration. Delete a bucket: ```bash -# delete bucket -mc rb rustfs/bucket-creation-by-mc -Removed `rustfs/bucket-creation-by-mc` successfully. +rc bucket remove rustfs/my-bucket +``` -# confirm bucket deletion -mc ls rustfs/bucket-creation-by-mc -mc: Unable to list folder. Bucket `bucket-creation-by-mc` does not exist. +```text +✓ Bucket 'rustfs/my-bucket' removed successfully. ``` ## Using the API diff --git a/content/management/bucket/images/bucket-creation-by-ui.png b/content/administration/data/bucket/images/bucket-creation-by-ui.png similarity index 100% rename from content/management/bucket/images/bucket-creation-by-ui.png rename to content/administration/data/bucket/images/bucket-creation-by-ui.png diff --git a/content/management/bucket/images/bucket-deletion-on-ui.png b/content/administration/data/bucket/images/bucket-deletion-on-ui.png similarity index 100% rename from content/management/bucket/images/bucket-deletion-on-ui.png rename to content/administration/data/bucket/images/bucket-deletion-on-ui.png diff --git a/content/administration/data/bucket/images/bucket-quota-console.png b/content/administration/data/bucket/images/bucket-quota-console.png new file mode 100644 index 00000000..5e46df45 Binary files /dev/null and b/content/administration/data/bucket/images/bucket-quota-console.png differ diff --git a/content/administration/data/bucket/meta.json b/content/administration/data/bucket/meta.json new file mode 100644 index 00000000..39583c1f --- /dev/null +++ b/content/administration/data/bucket/meta.json @@ -0,0 +1,10 @@ +{ + "title": "Bucket", + "pages": [ + "creation", + "deletion", + "quota", + "replication", + "policy" + ] +} \ No newline at end of file diff --git a/content/administration/data/bucket/policy.md b/content/administration/data/bucket/policy.md new file mode 100644 index 00000000..c13c11b5 --- /dev/null +++ b/content/administration/data/bucket/policy.md @@ -0,0 +1,205 @@ +--- +title: "Bucket Policy" +description: "Create, apply, inspect, and verify RustFS bucket policies with the S3 API." +--- + +RustFS bucket policies are S3-compatible resource policies attached directly to a bucket. Use them to grant or deny access to the bucket and its objects, including controlled anonymous access for public downloads. + +## Overview + +A bucket policy contains one or more statements that match a principal, action, resource, and optional conditions. RustFS evaluates the policy for requests to the bucket before allowing the storage operation. + +Bucket policies and IAM policies serve different purposes: + +| Policy type | Attached to | Typical use | +| --- | --- | --- | +| Bucket policy | A bucket | Grant public access, add resource-level restrictions, or authorize access to one bucket. | +| IAM policy | A user or group | Define what an authenticated identity can do across one or more resources. | + +An explicit `Deny` takes precedence over an `Allow`. Bucket owners remain able to get, replace, or delete the bucket policy so that a deny statement cannot permanently lock policy administration. + +RustFS implements the standard S3 operations `PutBucketPolicy`, `GetBucketPolicy`, `GetBucketPolicyStatus`, and `DeleteBucketPolicy`. + +:::warning[Public policies] + +A statement with `"Principal": "*"` can grant access without authentication. Keep the action and resource scope as narrow as possible, and verify the result anonymously before using the policy in production. + +::: + +## Configuration + +### Requirements + +- Create the target bucket before applying a policy. +- Configure the AWS CLI with a RustFS credential and region `us-east-1`. +- Use a credential allowed to perform the required policy-management action. + +Set reusable variables: + +```bash +export RUSTFS_ENDPOINT=http://localhost:9000 +export BUCKET_NAME=my-bucket +``` + +Policy management requires these actions: + +| Operation | Required action | +| --- | --- | +| Apply or replace a policy | `s3:PutBucketPolicy` | +| Read a policy | `s3:GetBucketPolicy` | +| Read public status | `s3:GetBucketPolicyStatus` | +| Remove a policy | `s3:DeleteBucketPolicy` | + +### Policy document structure + +A bucket policy uses version `2012-10-17` and a `Statement` array: + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "PublicReadObjects", + "Effect": "Allow", + "Principal": "*", + "Action": ["s3:GetObject"], + "Resource": ["arn:aws:s3:::my-bucket/public/*"] + } + ] +} +``` + +The bucket ARN and object ARN are different: + +| Resource | ARN format | Example actions | +| --- | --- | --- | +| Bucket | `arn:aws:s3:::my-bucket` | `s3:ListBucket`, `s3:GetBucketLocation` | +| Objects | `arn:aws:s3:::my-bucket/*` | `s3:GetObject`, `s3:PutObject`, `s3:DeleteObject` | + +Use a prefix in the object ARN, such as `arn:aws:s3:::my-bucket/public/*`, to limit access to part of the bucket. For the complete statement format and supported condition operators, see [Users, Groups, and Policies](/security-compliance/iam/policies#policy-document-format). + +### Public Access Block + +If the bucket's Public Access Block configuration has `BlockPublicPolicy` enabled, RustFS rejects a new policy containing an `Allow` statement with a wildcard principal. Keep this protection enabled unless anonymous access is intentional. + +Public Access Block does not replace careful policy review. An existing explicit `Deny` still overrides allows, and authenticated requests continue to be evaluated against IAM and bucket policies. + +## Usage + +### Create a public-read policy + +Create a policy that allows anonymous downloads only from the `public/` prefix: + +```bash +cat > /tmp/my-bucket-policy.json <<'EOF' +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "PublicReadObjects", + "Effect": "Allow", + "Principal": "*", + "Action": ["s3:GetObject"], + "Resource": ["arn:aws:s3:::my-bucket/public/*"] + } + ] +} +EOF +``` + +This policy does not allow anonymous bucket listing, uploads, overwrites, or deletes. + +### Apply the policy + +```bash +aws s3api put-bucket-policy \ + --bucket "$BUCKET_NAME" \ + --policy file:///tmp/my-bucket-policy.json \ + --endpoint-url "$RUSTFS_ENDPOINT" +``` + +Applying another policy replaces the complete existing policy. Retrieve and review the current document before updating it; statements are not merged automatically. + +### Read the policy + +```bash +aws s3api get-bucket-policy \ + --bucket "$BUCKET_NAME" \ + --endpoint-url "$RUSTFS_ENDPOINT" \ + --query Policy \ + --output text +``` + +RustFS preserves the submitted policy JSON. If no policy exists, the API returns `NoSuchBucketPolicy`. + +### Check public status + +```bash +aws s3api get-bucket-policy-status \ + --bucket "$BUCKET_NAME" \ + --endpoint-url "$RUSTFS_ENDPOINT" +``` + +The response contains a high-level public-access status: + +```json +{ + "PolicyStatus": { + "IsPublic": false + } +} +``` + +The current status check detects anonymous bucket listing (`s3:ListBucket`) and uploads (`s3:PutObject`). A policy that exposes only object downloads, such as the `public/` example above, can still report `false`. Always test the exact anonymous action, object prefix, and conditions instead of treating this status as a complete access analysis. + +### Remove the policy + +```bash +aws s3api delete-bucket-policy \ + --bucket "$BUCKET_NAME" \ + --endpoint-url "$RUSTFS_ENDPOINT" +``` + +Deleting a bucket policy removes only that resource policy. IAM policies attached to users and groups remain unchanged. + +## Verification + +Upload a test object with an authenticated credential: + +```bash +printf 'hello from RustFS\n' > /tmp/hello.txt + +aws s3api put-object \ + --bucket "$BUCKET_NAME" \ + --key public/hello.txt \ + --body /tmp/hello.txt \ + --endpoint-url "$RUSTFS_ENDPOINT" +``` + +Verify that the allowed object can be downloaded without credentials: + +```bash +curl --fail-with-body \ + "${RUSTFS_ENDPOINT}/${BUCKET_NAME}/public/hello.txt" +``` + +Then verify that an object outside the allowed prefix is not public: + +```bash +aws s3api put-object \ + --bucket "$BUCKET_NAME" \ + --key private/hello.txt \ + --body /tmp/hello.txt \ + --endpoint-url "$RUSTFS_ENDPOINT" + +curl --fail-with-body \ + "${RUSTFS_ENDPOINT}/${BUCKET_NAME}/private/hello.txt" +``` + +The second `curl` request should fail with `AccessDenied`. Also test each authenticated user role that relies on the policy, especially when the document contains conditions or explicit deny statements. + +## Next steps + +- [Manage IAM policies](/security-compliance/iam/policies) +- [Manage credentials](/operations/credentials) +- [Configure audit logs](/security-compliance/audit-logs) \ No newline at end of file diff --git a/content/administration/data/bucket/quota.md b/content/administration/data/bucket/quota.md new file mode 100644 index 00000000..ef975aac --- /dev/null +++ b/content/administration/data/bucket/quota.md @@ -0,0 +1,204 @@ +--- +title: "Bucket Quota" +description: "Configure, inspect, and verify hard storage quotas for individual RustFS buckets." +--- + +RustFS bucket quotas limit the total object data stored in an individual bucket. Use a quota to prevent one workload from consuming more than its assigned capacity while allowing other buckets to use the remaining storage. + +## Overview + +RustFS currently supports hard, byte-based quotas. Before accepting a write, RustFS compares the bucket's current usage plus the requested object size with the configured limit. A write that would exceed the limit is rejected with `InvalidRequest` and a `Bucket quota exceeded` message. + +Quota checks cover these operations: + +| Operation | Quota behavior | +| --- | --- | +| Upload an object | Rejects the upload when current usage plus the object size exceeds the limit. | +| Complete a multipart upload | Checks the completed object size before committing the upload. | +| Copy an object into the bucket | Checks the source object size against the destination bucket quota. | +| Delete an object | Always allowed so that you can recover capacity. | + +The quota applies to object data, not the number of objects or request rate. A bucket without a configured limit is unlimited. + +:::note[Replacement uploads] + +The quota check reserves the full incoming object size against the bucket's current usage. Replacing an existing key can therefore require enough headroom for the complete replacement object. + +::: + +## Configure in the Console + +1. Sign in to the RustFS Console and open **Browser**. +2. Find the bucket and select **Settings**. +3. Under **Capacity & Metadata**, find **Bucket Quota** and select **Edit**. +4. Enable **Bucket Quota**. +5. Enter the quota size and select **MiB**, **GiB**, **TiB**, or **PiB**. +6. Select **Save Quota**. + +![Set Bucket Quota dialog with quota size and unit controls](./images/bucket-quota-console.png) + +To remove the limit, open the dialog again, disable **Bucket Quota**, and save the change. + +## Use rc + +Configure an alias for your RustFS deployment: + +```bash +rc alias set rustfs http://localhost:9000 \ + \ + --region us-east-1 --bucket-lookup path +``` + +Replace `localhost` with the RustFS server address when `rc` runs on another host. + +### Permissions + +Quota operations require these policy actions: + +| Operation | Required action | +| --- | --- | +| Set or clear a quota | `admin:SetBucketQuota` | +| Read quota configuration or statistics | `s3:GetBucketQuota` | +| Check whether a proposed operation fits | `s3:GetBucketQuota` | + +The root credential has these permissions. For delegated administration, attach only the actions needed for the workflow. + +### Set a quota + +Set a 1 GiB hard quota. `rc` accepts byte values or units such as `1G`, `500M`, and `10KB`: + +```bash +rc bucket quota set rustfs/my-bucket 1G +``` + +The response includes the configured limit and current usage: + +```text +Bucket: my-bucket +Quota: 1 GiB +Usage: 0 B +Type: HARD +``` + +### Read the quota + +```bash +rc bucket quota info rustfs/my-bucket +``` + +Use `--json` when another tool needs to process the result: + +```bash +rc bucket quota info rustfs/my-bucket --json +``` + +When no limit is configured, the human-readable output reports `Quota: unlimited`. + +:::note[Configuration propagation] + +A query issued immediately after setting or clearing a quota can briefly return the previous state. Query the quota again and confirm the expected value before starting the verification workflow. + +::: + +### Clear a quota + +Remove the limit without deleting objects: + +```bash +rc bucket quota clear rustfs/my-bucket +rc bucket quota info rustfs/my-bucket +``` + +The bucket becomes unlimited after the configuration change is applied. + +## Advanced quota checks + +`rc 0.1.29` does not expose commands for detailed usage statistics or advisory checks for proposed writes. Use the RustFS Admin API for these operations. Requests must use AWS Signature Version 4 with an active RustFS credential. + +The examples use these shell variables: + +```bash +export RUSTFS_ENDPOINT=http://localhost:9000 +export RUSTFS_ACCESS_KEY= +export RUSTFS_SECRET_KEY= +export BUCKET_NAME=my-bucket +``` + +:::warning[Protect credentials] + +Environment variables are convenient for local testing but may be visible to processes running as the same operating-system user. Use your platform's secret manager or a restricted credentials file in production. + +::: + +### Read detailed usage statistics + +Use the statistics endpoint to retrieve the limit, current usage, remaining bytes, and percentage used: + +```bash +curl --fail-with-body \ + --aws-sigv4 "aws:amz:us-east-1:s3" \ + --user "${RUSTFS_ACCESS_KEY}:${RUSTFS_SECRET_KEY}" \ + "${RUSTFS_ENDPOINT}/rustfs/admin/v3/quota-stats/${BUCKET_NAME}" +``` + +```json +{ + "bucket": "my-bucket", + "quota_limit": 1073741824, + "current_usage": 1048576, + "remaining_quota": 1072693248, + "usage_percentage": 0.09765625 +} +``` + +Usage values come from RustFS data-usage accounting. Allow time for that view to reflect very recent changes before using the statistics response for external billing or orchestration. + +### Check a proposed upload + +Check whether a 64 MiB upload would fit without writing an object: + +```bash +curl --fail-with-body \ + --aws-sigv4 "aws:amz:us-east-1:s3" \ + --user "${RUSTFS_ACCESS_KEY}:${RUSTFS_SECRET_KEY}" \ + --request POST \ + --header "Content-Type: application/json" \ + --data '{"operation_type":"PUT","operation_size":67108864}' \ + "${RUSTFS_ENDPOINT}/rustfs/admin/v3/quota-check/${BUCKET_NAME}" +``` + +The `allowed` field reports the decision. This check is advisory: another write can consume capacity before the planned upload starts, so the actual upload remains authoritative. + +## Verification + +Set a small test quota, upload an object that fits, and then attempt to upload an object that exceeds the remaining capacity: + +```bash +rc bucket quota set rustfs/my-bucket 1M + +dd if=/dev/zero of=/tmp/quota-small.bin bs=1024 count=256 +dd if=/dev/zero of=/tmp/quota-large.bin bs=1048576 count=2 + +rc object copy /tmp/quota-small.bin rustfs/my-bucket/hello.bin +rc object copy /tmp/quota-large.bin rustfs/my-bucket/too-large.bin +``` + +The 256 KiB object succeeds. The 2 MiB object exceeds the 1 MiB bucket quota, so `rc` exits with an error and RustFS does not create `too-large.bin`. + +Delete the first object and clear the test quota: + +```bash +rc object remove rustfs/my-bucket/hello.bin --force +rc bucket quota clear rustfs/my-bucket +rc bucket quota info rustfs/my-bucket +``` + +Confirm that the final query reports `Quota: unlimited`. + +If quota enforcement cannot read or parse its internal configuration, RustFS logs `Bucket quota check degraded to allow` and permits the write. Monitor for this warning because it means quota enforcement is temporarily unavailable. + +## Next steps + +- [Create a bucket](./creation.md) +- [Manage lifecycle rules](../lifecycle-management.md) +- [Configure observability](/operations/observability) \ No newline at end of file diff --git a/content/administration/data/bucket/replication.md b/content/administration/data/bucket/replication.md new file mode 100644 index 00000000..37642ae9 --- /dev/null +++ b/content/administration/data/bucket/replication.md @@ -0,0 +1,298 @@ +--- +title: "Bucket Replication" +description: "Configure, validate, and monitor asynchronous replication between versioned RustFS buckets." +--- + +RustFS bucket replication copies selected object versions from a source bucket to a target bucket. Use it to maintain a remote copy of bucket data, distribute objects between deployments, or prepare a secondary copy for recovery workflows. + +## Overview + +Bucket replication has two configuration layers: + +1. A **remote target** stores the target endpoint, bucket, credentials, and generated target ARN. +2. An S3 **replication configuration** attaches rules to the source bucket and references that target ARN. + +Both the source and target buckets must have versioning enabled. RustFS verifies the target connection and target-bucket versioning when you register the remote target. It rejects a replication configuration whose enabled rules reference an unknown or stale target ARN. + +Replication is asynchronous by default. A successful source upload means RustFS accepted the source object; it does not mean the target copy is already complete. + +Rules can select objects by prefix or object tags and control these behaviors: + +| Rule setting | Behavior | +| --- | --- | +| `Status` | Enables or disables the rule. | +| `Filter` | Restricts replication by key prefix, object tags, or both. | +| `ExistingObjectReplication` | Includes objects that existed before the rule when set to `Enabled`. | +| `DeleteMarkerReplication` | Replicates delete markers when set to `Enabled`. | +| `DeleteReplication` | Replicates deletion of a specific object version when set to `Enabled`. | +| `Destination` | Identifies the registered remote target by ARN. | + +Bucket replication is directional. Configure a separate target and rule in the opposite direction if both buckets must accept writes and replicate them to each other. Do not confuse bucket replication with [site replication](/operations/high-availability/site-replication), which synchronizes broader site configuration and identity data. + +## Configuration + +### Requirements + +- A source RustFS deployment and a reachable target S3-compatible deployment. +- A source bucket and target bucket with versioning enabled. +- A dedicated target credential that can inspect the target bucket's versioning and write replicated object versions and delete markers. +- A source administrator allowed to manage remote targets and replication configuration. +- Network access from every source node that can run replication work to the target endpoint. +- The RustFS [`rc`](/operations/rc) client installed on an administration host. + +Configure one `rc` alias for each deployment. Use dedicated credentials and replace the example endpoints before running the commands: + +```bash +rc alias set source https://source.example.com:9000 \ + \ + --region us-east-1 --bucket-lookup path + +rc alias set target https://target.example.com:9000 \ + \ + --region us-east-1 --bucket-lookup path + +rc alias list +``` + +The source deployment uses the target alias credentials when it registers the remote target. `rc alias list` displays endpoints but does not print secret keys. + +:::warning[Protect target credentials] + +RustFS stores the target credential as part of the source bucket's remote-target configuration. Use a dedicated credential with access limited to the target bucket and do not reuse a root credential. + +::: + +### Permissions + +Source-side administration uses these policy actions: + +| Operation | Required action | +| --- | --- | +| Register, update, or remove a remote target | `admin:SetBucketTarget` | +| List remote targets | `admin:GetBucketTarget` | +| Read replication metrics | `admin:GetReplicationMetrics` | +| Apply or delete replication configuration | `s3:PutReplicationConfiguration` | +| Read replication configuration | `s3:GetReplicationConfiguration` | + +The target credential must pass RustFS target validation, which checks bucket access, versioning, replicated-object writes, replicated delete markers, and object-version deletion. When Object Lock is enabled on the source bucket, the target must have compatible Object Lock support. + +### Create and version the buckets + +Create the source bucket: + +```bash +rc bucket create source/my-bucket +``` + +Create the target bucket: + +```bash +rc bucket create target/my-bucket-replica +``` + +Enable versioning on both buckets: + +```bash +rc bucket version enable source/my-bucket +rc bucket version enable target/my-bucket-replica + +rc bucket version info source/my-bucket +rc bucket version info target/my-bucket-replica +``` + +Do not suspend source-bucket versioning while replication is configured. + +## Usage + +### Configure replication in the Console + +The RustFS Console combines remote-target registration and replication-rule configuration in one form. + +1. Sign in to the source deployment's Console. +2. Open **Buckets**, locate the source bucket, and select **Settings**. +3. Under **Data Protection**, enable **Versioning** if it is disabled. +4. Under **Automation**, select **Open Bucket Replication**. +5. Select **Add Replication Rule**. +6. Configure the destination and rule: + +| Console field | Value | +| --- | --- | +| **Priority** | Rule evaluation priority. The initial value is `1`. | +| **Mode** | Select **Asynchronous** or **Synchronous**. Asynchronous is selected by default. | +| **Endpoint** | Target S3 API address as `host:port`, without a URL scheme. | +| **Bucket** | Versioned destination bucket name. | +| **Access Key** and **Secret Key** | Dedicated credentials authorized to replicate into the destination bucket. | +| **Region** | Target region. The initial value is `us-east-1`. | +| **Storage Class** | Storage class applied at the destination. The initial value is `STANDARD`. | +| **Prefix** | Optional key prefix used to limit matching objects. | +| **Tags** | Optional object-tag name and value filters. Select **Add Tag** for additional filters. | +| **Use TLS** | Enables HTTPS for the destination connection. | +| **Replicate Existing Objects** | Includes objects created before the rule. Enabled by default. | +| **Replicate Delete Markers** | Copies delete markers to the destination. Enabled by default. | +| **Replicate Delete** | Copies deletion of a specific object version. Enabled by default. | +| **Health Check Interval** | Target health-check interval in seconds. The initial value is `60`. | +| **Bandwidth Limit** | Per-target transfer limit, selectable in KiB/s, MiB/s, or GiB/s. | + +7. Select **Save**. RustFS validates the source bucket, target connection, target credentials, and target-bucket versioning before accepting the rule. +8. Return to **Bucket Replication** to review the rule or select **Refresh** to update its displayed state. + +:::warning[Enable versioning on both deployments] + +The source and destination buckets must both have versioning enabled before you save the rule. Sign in to the target deployment's Console and enable versioning under **Buckets** → **Settings** → **Data Protection** when needed. + +::: + +For repeatable automation or configuration management, use the `rc` workflow below. + +### Add a replication rule with rc + +Create an asynchronous rule that copies new and existing objects and propagates delete markers and explicit version deletions: + +```bash +rc bucket replication add source/my-bucket \ + --remote-bucket target/my-bucket-replica \ + --id replicate-all \ + --priority 1 \ + --replicate delete,delete-marker,existing-objects +``` + +`rc` registers the target, obtains its generated ARN, and applies the replication rule in one operation. Omit `--replicate` flags for behaviors you do not want. Add `--sync` only when writes must wait for synchronous replication. + +To limit replication to a prefix, add `--prefix`: + +```bash +rc bucket replication add source/my-bucket \ + --remote-bucket target/my-bucket-replica \ + --id replicate-documents \ + --priority 2 \ + --prefix documents/ \ + --replicate delete-marker,existing-objects +``` + +Objects that do not match an enabled rule remain only in the source bucket. Use `--bandwidth` to set a byte-per-second limit, `--healthcheck-seconds` to change the target health-check interval, and `--storage-class` to override the destination storage class. + +### List and update rules + +List the active rules: + +```bash +rc bucket replication list source/my-bucket +rc bucket replication list source/my-bucket --json +``` + +Update a rule by its ID. Only supplied settings are changed: + +```bash +rc bucket replication update source/my-bucket \ + --id replicate-all \ + --priority 2 \ + --bandwidth 104857600 \ + --healthcheck-seconds 60 +``` + +Use `--status Enabled|Disabled` to enable or disable a rule and `--sync true|false` to change its replication mode. + +### Export and import configuration + +Export the complete replication configuration for review or backup: + +```bash +rc bucket replication export source/my-bucket --json > replication.json +``` + +The export includes remote-target metadata and the target Access Key, although the Secret Key is omitted. Protect the file as sensitive configuration. + +Import a previously exported configuration: + +```bash +rc bucket replication import source/my-bucket replication.json +``` + +### Delete replication configuration + +```bash +rc bucket replication remove source/my-bucket --id replicate-all + +# Remove every replication rule from the bucket. +rc bucket replication remove source/my-bucket --all +``` + +RustFS also removes replication remote targets referenced by the deleted configuration. It does not delete objects or versions already copied to the target bucket. Remove or retain those objects according to the target bucket's lifecycle and retention requirements. + +:::note[rc 0.1.29 removal response] + +After RustFS removes the replication configuration and its targets, `rc 0.1.29` can report `Remote target not found` while attempting a second target cleanup. Run `rc bucket replication list source/my-bucket --json`; an empty `rules` array confirms that the configuration was removed. + +::: + +## Verification + +### Check target readiness + +`rc bucket replication add` checks source access, target connectivity, target credentials, and bucket versioning before it creates the rule. Confirm the resulting destination and rule settings: + +```bash +rc bucket replication list source/my-bucket --json +``` + +### Replicate an object + +Upload a test object to the source: + +```bash +printf 'hello from RustFS replication\n' > /tmp/hello.txt +rc object copy /tmp/hello.txt source/my-bucket/hello.txt +``` + +Inspect the source object: + +```bash +rc object stat source/my-bucket/hello.txt --json +``` + +Because replication is asynchronous by default, the target object might not appear immediately. Repeat this command until it succeeds: + +```bash +rc object stat target/my-bucket-replica/hello.txt --json +rc object show target/my-bucket-replica/hello.txt +``` + +Compare the source and target `etag` and `size_bytes` values, then confirm that `object show` returns the expected content. + +To verify delete-marker replication, delete the source object and list versions on both buckets: + +```bash +rc object remove source/my-bucket/hello.txt --force + +rc bucket version list source/my-bucket/hello.txt --json +rc bucket version list target/my-bucket-replica/hello.txt --json +``` + +When delete-marker replication succeeds, the latest entry on both buckets has `is_delete_marker: true`. + +### Inspect replication metrics + +```bash +rc bucket replication status source/my-bucket +rc bucket replication status source/my-bucket --json +``` + +The command returns the source node's current in-memory replication statistics. Metrics can remain zero even after an object reaches the destination, so use them together with target-object and version checks rather than as the only verification signal. + +### Troubleshoot failures + +| Symptom | Check | +| --- | --- | +| `bucket versioning must be enabled` | Enable versioning on the source bucket. | +| Target registration reports `not versioned` | Enable versioning on the target bucket. | +| `replication target configuration not found` | Recheck the target alias, credentials, and destination bucket, then rerun `replication add`. | +| Rule reports a stale target | Refresh the rule list and retry `replication add`; if it persists, remove the failed configuration and recreate the target. | +| Target object does not appear | Check target reachability, credentials, target quota, and `rc bucket replication status`. | +| Replication fails | Inspect source-node logs for the target ARN and object key. | +| Target removal is disallowed | Delete or replace the replication configuration before removing a referenced target. | + +## Next steps + +- [Manage bucket lifecycle](../lifecycle-management.md) +- [Configure bucket quotas](./quota.md) +- [Configure observability](/operations/observability) \ No newline at end of file diff --git a/content/administration/data/lifecycle-management.md b/content/administration/data/lifecycle-management.md new file mode 100644 index 00000000..9cc05f87 --- /dev/null +++ b/content/administration/data/lifecycle-management.md @@ -0,0 +1,145 @@ +--- +title: "Lifecycle Management" +description: "Configure, inspect, and maintain RustFS object lifecycle rules with the rc command-line client." +--- + +RustFS lifecycle management applies expiration and transition rules to objects in a bucket. This page shows how to manage those rules with `rc`, verify a transition, and restore a temporary local copy of transitioned data. + +Before you begin, [install `rc`](/operations/rc), configure an alias named `local`, and create `my-bucket`. To transition objects, first register the target in [Tiered Storage](/administration/data/tiered-storage) and note its uppercase tier name. + +:::note[Asynchronous evaluation] + +Lifecycle rules do not process every eligible object immediately. The [Object Scanner](/administration/data/object/scanner) evaluates lifecycle work in the background. + +::: + +## Add an expiration rule + +Expire objects under the `logs/` prefix 30 days after creation: + +```bash +rc bucket lifecycle rule add local/my-bucket \ + --prefix logs/ \ + --expiry-days 30 +``` + +The command creates an enabled rule and returns its generated rule ID. Record that ID when you need to edit or remove this specific rule. + +## Add a transition rule + +Move object data to the registered `COLDTIER` tier after 90 days: + +```bash +rc bucket lifecycle rule add local/my-bucket \ + --transition-days 90 \ + --storage-class COLDTIER +``` + +`--storage-class` must match a registered tier name. Adding the rule does not create the tier or validate an AWS storage-class label as a RustFS tier. + +You can combine expiration and transition options in one rule. Use the noncurrent-version options only for a versioned bucket: + +```bash +rc bucket lifecycle rule add local/my-bucket \ + --noncurrent-transition-days 30 \ + --noncurrent-transition-storage-class COLDTIER \ + --noncurrent-expiry-days 365 +``` + +## Inspect and update rules + +List the current rules and note the ID of the rule you want to change: + +```bash +rc bucket lifecycle rule list local/my-bucket +``` + +Change a rule's expiration period or disable it without deleting it: + +```bash +rc bucket lifecycle rule edit local/my-bucket \ + --id \ + --expiry-days 60 +rc bucket lifecycle rule edit local/my-bucket \ + --id \ + --disable true +``` + +Remove one rule by ID. Use `--all` only when you intend to remove the bucket's complete lifecycle configuration: + +```bash +rc bucket lifecycle rule remove local/my-bucket --id +rc bucket lifecycle rule remove local/my-bucket --all +``` + +## Export and import rules + +Export the bucket's lifecycle configuration before a bulk change: + +```bash +rc bucket lifecycle rule export local/my-bucket > lifecycle.json +``` + +Importing a file replaces the lifecycle configuration sent to the bucket. Review the JSON before applying it: + +```bash +rc bucket lifecycle rule import local/my-bucket lifecycle.json +``` + +## Confirm a lifecycle transition + +After an object becomes eligible under its lifecycle rule, inspect it on the source cluster: + +```bash +aws s3api head-object \ + --bucket my-bucket \ + --key hello.txt \ + --endpoint-url http://localhost:9000 +``` + +After the transition completes, the response reports the registered tier name as the storage class: + +```json +{ + "StorageClass": "COLDTIER" +} +``` + +The response can contain additional object metadata. Before the transition completes, `StorageClass` may be absent or may not yet report `COLDTIER`. + +Read the object through the source RustFS endpoint as usual: + +```bash +aws s3 cp \ + s3://my-bucket/hello.txt \ + /path/to/hello.txt \ + --endpoint-url http://localhost:9000 +``` + +RustFS reads transitioned data through the source bucket and object key. Applications do not need to address the target bucket directly. + +## Restore a local copy + +Request a temporary local copy of a transitioned object and retain it for seven days: + +```bash +rc bucket lifecycle restore local/my-bucket/hello.txt --days 7 +``` + +While the copy-back is running, `HEAD` reports `x-amz-restore: ongoing-request="true"`. After completion, it reports `ongoing-request="false"` with an expiry date. A second restore submitted while one is running returns `RestoreAlreadyInProgress`. + +When the restore period expires, RustFS removes the local restored copy and its restore metadata. The transitioned object remains available from the remote tier. + +## Command compatibility + +The latest `rc` also accepts `rc ilm` as a compatibility alias. We recommend the noun-first `rc bucket lifecycle` form for new commands and scripts: + +```bash +rc bucket lifecycle --help +rc bucket lifecycle rule --help +rc bucket lifecycle tier --help +``` + +## Next steps + +Review [Tiered Storage](/administration/data/tiered-storage) to monitor or maintain remote tiers, and use [object creation](/administration/data/object/creation) to create test objects for a lifecycle rule. diff --git a/content/administration/data/meta.json b/content/administration/data/meta.json new file mode 100644 index 00000000..5668022a --- /dev/null +++ b/content/administration/data/meta.json @@ -0,0 +1,9 @@ +{ + "title": "Data Management", + "pages": [ + "object", + "bucket", + "tiered-storage", + "lifecycle-management" + ] +} \ No newline at end of file diff --git a/content/management/object/creation.md b/content/administration/data/object/creation.md similarity index 68% rename from content/management/object/creation.md rename to content/administration/data/object/creation.md index aa82ab35..b0c5ca07 100644 --- a/content/management/object/creation.md +++ b/content/administration/data/object/creation.md @@ -1,19 +1,17 @@ --- title: "Object Creation" -description: "Create objects using the RustFS UI, MinIO Client, or API." +description: "Create objects using the RustFS UI, rc, or the S3 API." --- Objects are the fundamental storage units in RustFS, containing data, metadata, and a unique key. This guide covers object creation (upload). -> For concepts related to Objects, see [Core Concepts](../../concepts/glossary.md). +## Requirements -## Creating Objects - -Prerequisites: - -- A running RustFS instance (see [Installation Guide](../../installation/index.md)). +- A running RustFS instance (see [Installation Guide](../../../installation/index.md)). +- [`rc`](/operations/rc) installed and configured with an alias for the command-line workflow. +- A target bucket. Create one by following [Bucket Creation](../bucket/creation.md). -[Create a bucket](../bucket/creation.md), then upload files to it. +## Creating Objects ### Using the RustFS UI @@ -28,20 +26,15 @@ Click on an object to view its details. ![object details info](images/object_details_info.png) -### Using `mc` +### Using `rc` -> See the [`mc` Usage Guide](../../developer/mc.md) for installation and configuration. +See the [`rc` guide](/operations/rc) for installation and alias configuration. Upload a file: ```bash -# upload file -mc cp 1.txt rustfs/bucket-creation-by-mc -/tmp/1.txt: 13 B / 13 B ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 61 B/s 0s% - -# confirm file uploaded -mc ls rustfs/bucket-creation-by-mc -[2025-08-01 10:01:08 CST] 13B 1.txt +rc object copy /path/to/hello.txt rustfs/my-bucket/hello.txt +rc object list rustfs/my-bucket ``` Verify the upload in the RustFS Console. diff --git a/content/management/object/deletion.md b/content/administration/data/object/deletion.md similarity index 69% rename from content/management/object/deletion.md rename to content/administration/data/object/deletion.md index 9cd993b9..5e310623 100644 --- a/content/management/object/deletion.md +++ b/content/administration/data/object/deletion.md @@ -1,11 +1,14 @@ --- title: "Object Deletion" -description: "Delete objects using the RustFS UI, MinIO Client, or API." +description: "Delete objects using the RustFS UI, rc, or the S3 API." --- This guide covers object deletion. -> For concepts related to objects, see [Core Concepts](../../concepts/glossary.md). +## Requirements + +- Install and configure [`rc`](/operations/rc) before using the command-line workflow. +- Confirm the alias, bucket, and object key before deleting an object. ## Using the RustFS UI @@ -16,22 +19,23 @@ This guide covers object deletion. ![object deletion from ui](images/delete_file_from_ui.png) -### Using `mc` +## Using `rc` Delete a file: ```bash -# delete file -mc rm rustfs/bucket-creation-by-mc/1.txt -Removed `rustfs/bucket-creation-by-mc/1.txt`. +rc object remove rustfs/my-bucket/hello.txt +rc object list rustfs/my-bucket +``` -# confirm deletion -mc ls rustfs/bucket-creation-by-mc/1.txt +```text +Removed: rustfs/my-bucket/hello.txt +✓ Removed 1 object(s). ``` Verify the deletion in the RustFS Console. -### Using the API +## Using the API Delete a file via API: diff --git a/content/management/object/images/delete_file_from_ui.png b/content/administration/data/object/images/delete_file_from_ui.png similarity index 100% rename from content/management/object/images/delete_file_from_ui.png rename to content/administration/data/object/images/delete_file_from_ui.png diff --git a/content/administration/data/object/images/multipart-upload-console.png b/content/administration/data/object/images/multipart-upload-console.png new file mode 100644 index 00000000..5276735c Binary files /dev/null and b/content/administration/data/object/images/multipart-upload-console.png differ diff --git a/content/administration/data/object/images/object-lock-create-bucket.png b/content/administration/data/object/images/object-lock-create-bucket.png new file mode 100644 index 00000000..6264eb17 Binary files /dev/null and b/content/administration/data/object/images/object-lock-create-bucket.png differ diff --git a/content/management/object/images/object_details_info.png b/content/administration/data/object/images/object_details_info.png similarity index 100% rename from content/management/object/images/object_details_info.png rename to content/administration/data/object/images/object_details_info.png diff --git a/content/management/object/images/upload_file_from_ui.png b/content/administration/data/object/images/upload_file_from_ui.png similarity index 100% rename from content/management/object/images/upload_file_from_ui.png rename to content/administration/data/object/images/upload_file_from_ui.png diff --git a/content/administration/data/object/meta.json b/content/administration/data/object/meta.json new file mode 100644 index 00000000..5c8d87c5 --- /dev/null +++ b/content/administration/data/object/meta.json @@ -0,0 +1,11 @@ +{ + "title": "Object", + "pages": [ + "creation", + "deletion", + "object-lock", + "versioning", + "multipart-upload", + "scanner" + ] +} \ No newline at end of file diff --git a/content/administration/data/object/multipart-upload.md b/content/administration/data/object/multipart-upload.md new file mode 100644 index 00000000..d4a6af66 --- /dev/null +++ b/content/administration/data/object/multipart-upload.md @@ -0,0 +1,101 @@ +--- +title: "Multipart Upload" +description: "Upload large objects in parts, monitor Console upload tasks, and verify completed objects with rc." +--- + +Multipart upload splits one object into independently uploaded parts and assembles them on the server. Use it for large objects, retryable transfers, and browser uploads that need progress and cancellation controls. + +## Overview + +The S3 multipart workflow has three required phases: + +1. **CreateMultipartUpload** returns an upload ID. +2. **UploadPart** uploads numbered parts and returns an ETag for each part. +3. **CompleteMultipartUpload** submits the ordered part numbers and ETags to create the final object. + +Abort an unfinished upload with **AbortMultipartUpload** so temporary parts do not continue consuming storage. + +RustFS accepts part numbers from `1` through `10000`. `ListParts` and `ListMultipartUploads` return at most 1,000 entries per response and use markers for pagination. The completed object appears only after the completion request succeeds. + +## Upload in the Console + +1. Sign in to the RustFS Console and open the destination bucket. +2. Select **Upload File/Folder**. +3. Optionally enter a **Current Prefix**. +4. Select **Select File** or **Select Folder**, then choose the content to upload. +5. Review the selected names and sizes, then select **Start Upload**. + +![Console upload dialog with a selected large file](./images/multipart-upload-console.png) + +The observed Console upload dialog supports up to 10,000 selected files and reports a 512 GB maximum for a single file. These are Console upload limits; S3 clients can have different local limits. + +After upload starts, **Task Management** groups tasks into Pending, Processing, Completed, Failed, and Canceled states. Each processing task shows progress and a **Cancel** action. Canceling an active multipart task aborts the current part request and marks the task as canceled. + +Refresh the bucket after completion and confirm the object size and modification time. + +## Use rc + +Upload and verify an object with `rc`: + +```bash +rc object copy /path/to/hello.txt rustfs/my-bucket/hello.txt +rc object stat rustfs/my-bucket/hello.txt --json +``` + +:::note[rc 0.1.29 multipart boundary] + +`rc 0.1.29` does not expose create, upload-part, complete, list-parts, or abort multipart commands. A validated 20 MiB `rc object copy` used a single `PutObject` request rather than multipart upload. Use the Console or an S3 SDK when explicit multipart behavior is required; use `rc object stat` to verify the completed object. + +::: + +For a versioned bucket, list the version created by a completed multipart upload: + +```bash +rc bucket version list rustfs/my-bucket/hello.txt --json +``` + +RustFS returns a version ID when multipart completion succeeds in a versioned bucket. + +## S3 multipart operations + +Use an S3 SDK that supports these standard operations: + +| Phase | S3 operation | Required values | +| --- | --- | --- | +| Initiate | `CreateMultipartUpload` | Bucket, key, metadata, encryption, and optional Object Lock settings. | +| Upload | `UploadPart` | Bucket, key, upload ID, part number, and body. Save the returned ETag. | +| Inspect | `ListParts` | Bucket, key, and upload ID. Paginate when needed. | +| Complete | `CompleteMultipartUpload` | Ordered part numbers and their exact ETags. | +| Cancel | `AbortMultipartUpload` | Bucket, key, and upload ID. | +| Discover | `ListMultipartUploads` | Bucket and optional prefix. Paginate when needed. | + +Do not reuse an upload ID for a different key. Submit completed parts in ascending part-number order and preserve each ETag exactly as returned. + +## Verification + +After completion: + +```bash +rc object stat rustfs/my-bucket/hello.txt --json +rc object show rustfs/my-bucket/hello.txt > /tmp/hello-downloaded.txt +cmp /path/to/hello.txt /tmp/hello-downloaded.txt +``` + +Confirm that `size_bytes` matches the local file and that `cmp` exits successfully. For a versioned bucket, also confirm that `rc bucket version list` returns a version ID. + +## Troubleshooting + +| Symptom | Check | +| --- | --- | +| Upload remains in Processing | Check browser connectivity and keep the Console tab open; cancel and retry when the connection is interrupted. | +| A part request fails | Retry that part with the same upload ID and part number, then use the latest returned ETag. | +| Completion reports an invalid part | Verify the submitted part numbers, ordering, and ETags against `ListParts`. | +| Object is absent after uploading parts | Send `CompleteMultipartUpload`; uploaded parts alone do not create the object. | +| Temporary storage continues growing | List incomplete uploads and abort those that are no longer needed. | +| Completion fails on a locked key | Check Object Lock retention or Legal Hold on the current destination version. | + +## Next steps + +- [Create and inspect objects](./creation.md) +- [Manage object versions](./versioning.md) +- [Protect objects with Object Lock](./object-lock.md) \ No newline at end of file diff --git a/content/administration/data/object/object-lock.md b/content/administration/data/object/object-lock.md new file mode 100644 index 00000000..8e35fbf7 --- /dev/null +++ b/content/administration/data/object/object-lock.md @@ -0,0 +1,103 @@ +--- +title: "Object Lock" +description: "Protect versioned objects from deletion with retention periods and legal holds." +--- + +RustFS Object Lock applies write-once, read-many protection to individual object versions. Use retention periods for time-bound protection and Legal Hold for protection without a predefined expiration date. + +## Overview + +Object Lock requires bucket versioning and must be enabled when the bucket is created. Each overwrite creates a new version; retention and Legal Hold protect a specific version rather than the object key as a whole. + +| Protection | Behavior | +| --- | --- | +| `GOVERNANCE` retention | Blocks deletion and retention shortening unless the caller has bypass permission and explicitly requests a bypass. Retention can be extended without bypass. | +| `COMPLIANCE` retention | Blocks deletion even with governance bypass. The date can be extended but not shortened. | +| Legal Hold | Blocks deletion until the hold is set to `OFF`. It has no expiration date and governance bypass does not override it. | + +Deleting a key without a version ID creates a delete marker. The protected version remains stored and can still be retrieved by version ID. + +:::danger[Plan retention before enabling it] + +COMPLIANCE retention cannot be bypassed or shortened before its retain-until date. Test policies in a non-production bucket and verify time synchronization, permissions, lifecycle rules, replication, and backup procedures before protecting production data. + +::: + +## Configure in the Console + +1. Sign in to the RustFS Console and open **Buckets**. +2. Select **Create Bucket** and enter the bucket name. +3. Enable **Object Lock**. The Console also enables **Version** because Object Lock requires versioning. +4. To apply retention automatically to new object versions, enable **Retention**. +5. Select **COMPLIANCE** or **GOVERNANCE**, enter the validity, and select **Day** or **Year**. +6. Select **Create**. + +![Create Bucket dialog with versioning, Object Lock, and default retention enabled](./images/object-lock-create-bucket.png) + +The Console initially displays `180` days when default retention is enabled. Replace it with the period required by your retention policy; it is a UI initial value, not a general recommendation. + +To inspect a protected object, open the bucket, select the object name, and use the **Versions** and **Info** tabs. The Info tab exposes **Legal Hold** and **RetentionPolicy** fields. + +## Use rc + +Configure an alias before running these examples: + +```bash +rc alias set rustfs http://localhost:9000 \ + \ + --region us-east-1 --bucket-lookup path +``` + +:::note[rc 0.1.29 bucket creation boundary] + +`rc bucket create --with-lock` and `--with-versioning` appear in the `rc 0.1.29` help output but return `not implemented` when executed. Create the Object Lock bucket in the Console. The upload headers shown below are supported and were validated against RustFS. + +::: + +Upload an object version with GOVERNANCE retention. Supply both retention headers together and use an RFC 3339 UTC timestamp in the future: + +```bash +rc object copy /path/to/hello.txt rustfs/my-bucket/hello.txt \ + -H "x-amz-object-lock-mode:GOVERNANCE" \ + -H "x-amz-object-lock-retain-until-date:2027-01-01T00:00:00Z" +``` + +Upload an object version with Legal Hold enabled: + +```bash +rc object copy /path/to/hello.txt rustfs/my-bucket/legal-hold.txt \ + -H "x-amz-object-lock-legal-hold:ON" +``` + +List the protected versions and inspect a specific version: + +```bash +rc bucket version list rustfs/my-bucket --json +rc object stat rustfs/my-bucket/hello.txt \ + --version-id --json +``` + +`rc 0.1.29` does not provide commands to change retention or switch an existing Legal Hold between `ON` and `OFF`. Use the Console or an S3 SDK for those operations. + +## Verify protection + +1. Upload a test object with GOVERNANCE retention or Legal Hold. +2. Record its version ID with `rc bucket version list`. +3. Attempt to delete that exact version through an S3 client without bypass. RustFS must return `AccessDenied` while protection is active. +4. Confirm the version remains visible in the Console and through `rc object stat --version-id`. + +For GOVERNANCE retention, test bypass only with a dedicated administrative identity that has `s3:BypassGovernanceRetention`. COMPLIANCE retention and Legal Hold remain protected from governance bypass. + +## Operational considerations + +- Default bucket retention is calculated when a new object version or multipart upload is initiated. +- Copying an object creates a new destination version; destination retention policy applies independently. +- Suspending versioning is not appropriate for an Object Lock bucket. +- Lifecycle expiration cannot remove a version while retention or Legal Hold blocks deletion. +- Replication targets for locked objects must support compatible Object Lock behavior. + +## Next steps + +- [Manage object versions](./versioning.md) +- [Configure bucket replication](../bucket/replication.md) +- [Review the security checklist](/installation/requirement/checklists/security-checklists) \ No newline at end of file diff --git a/content/management/object/scanner.md b/content/administration/data/object/scanner.md similarity index 97% rename from content/management/object/scanner.md rename to content/administration/data/object/scanner.md index ac0b560d..6330c38f 100644 --- a/content/management/object/scanner.md +++ b/content/administration/data/object/scanner.md @@ -29,4 +29,4 @@ Similar to the `osd_scrub_begin_hour` configuration in Ceph, administrators can ## Monitoring and Metrics Scanner statistics include total task count, failure count, and time distribution, exposing metrics through the Prometheus data model such as `rustfs_scanner_jobs_total`, `rustfs_scanner_failures_total`, and `rustfs_scanner_duration_seconds`. -Combined with monitoring systems, alerts can be set based on scanning failure rates and duration to promptly discover and locate potential issues at the storage or network levels. +Combined with monitoring systems, alerts can be set based on scanning failure rates and duration to promptly discover and locate potential issues at the storage or network levels. \ No newline at end of file diff --git a/content/administration/data/object/versioning.md b/content/administration/data/object/versioning.md new file mode 100644 index 00000000..b891c756 --- /dev/null +++ b/content/administration/data/object/versioning.md @@ -0,0 +1,125 @@ +--- +title: "Versioning" +description: "Enable bucket versioning, recover earlier object versions, and manage delete markers with rc and the Console." +--- + +RustFS bucket versioning preserves multiple versions of the same object key. Use it to recover from accidental overwrites and deletes and to satisfy prerequisites for Object Lock and bucket replication. + +## Overview + +When versioning is enabled: + +- Each upload or copy to an existing key creates a new version ID. +- A normal delete creates a delete marker instead of removing older versions. +- Reading the key without a version ID returns the latest visible version, or `NotFound` when the latest entry is a delete marker. +- Reading or deleting a specific version requires its version ID. + +Suspending versioning stops normal creation of new version IDs but preserves existing versions and delete markers. Suspension is not the same as disabling or removing version history. + +## Configure in the Console + +### Enable at bucket creation + +1. Open **Buckets** and select **Create Bucket**. +2. Enter the bucket name. +3. Enable **Version**. +4. Select **Create**. + +### Enable or suspend an existing bucket + +1. Open **Buckets**, locate the bucket, and select **Settings**. +2. Under **Data Protection**, locate **Versioning**. +3. Enable versioning or suspend it as required. + +The settings page reports `Enabled`, `Suspended`, or `Disabled`. It also notes that suspension preserves existing versions. + +### Browse and recover versions + +1. Open the bucket and enable **Show Deleted Objects** when you need to see keys hidden by delete markers. +2. Select an object name to open **Object Details**. +3. Select **Versions** to inspect available versions and delete markers. +4. Download the required version and upload it again to make its content the latest version. + +## Use rc + +Create a bucket and enable versioning: + +```bash +rc bucket create rustfs/my-bucket +rc bucket version enable rustfs/my-bucket +rc bucket version info rustfs/my-bucket +``` + +Upload two versions of the same key: + +```bash +printf 'version one\n' > /tmp/hello.txt +rc object copy /tmp/hello.txt rustfs/my-bucket/hello.txt + +printf 'version two\n' > /tmp/hello.txt +rc object copy /tmp/hello.txt rustfs/my-bucket/hello.txt --overwrite +``` + +List versions and record the version ID you want to recover: + +```bash +rc bucket version list rustfs/my-bucket/hello.txt --json +rc object stat rustfs/my-bucket/hello.txt \ + --version-id --json +``` + +Restore an earlier version by downloading its body and uploading it as a new latest version: + +```bash +rc object show rustfs/my-bucket/hello.txt \ + --version-id > /tmp/hello-restored.txt + +rc object copy /tmp/hello-restored.txt \ + rustfs/my-bucket/hello.txt --overwrite +``` + +Delete the current key to create a delete marker, then inspect it: + +```bash +rc object remove rustfs/my-bucket/hello.txt --force +rc bucket version list rustfs/my-bucket/hello.txt --json +``` + +The latest entry should show `is_delete_marker: true`. The earlier versions remain available by version ID. + +Suspend versioning when you no longer want normal writes to create new numbered versions: + +```bash +rc bucket version suspend rustfs/my-bucket +rc bucket version info rustfs/my-bucket +``` + +:::warning[rc 0.1.29 version deletion boundary] + +`rc object remove --versions` is listed in the `rc 0.1.29` help output but returns `not implemented`. Use the Console or an S3 SDK to permanently delete selected version IDs. Do not remove an entire version history unless you have verified retention, replication, and recovery requirements. + +::: + +## Verification + +After enabling versioning: + +1. Upload the same key twice with different content. +2. Run `rc bucket version list` and confirm that both entries have distinct version IDs. +3. Retrieve each version with `rc object show --version-id` and compare its content. +4. Delete the key without a version ID and confirm that a delete marker appears. +5. Retrieve an older version by version ID to confirm it remains recoverable. + +## Operational considerations + +- Versioning increases storage use because overwrites and deletes retain older data. +- Configure lifecycle rules for noncurrent versions only after defining recovery and retention periods. +- Bucket replication requires versioning on both source and destination buckets. +- Object Lock depends on versioning and protects individual versions. +- Multipart completion creates one new object version in a versioned bucket. + +## Next steps + +- [Protect versions with Object Lock](./object-lock.md) +- [Upload large objects](./multipart-upload.md) +- [Manage bucket lifecycle](../lifecycle-management.md) \ No newline at end of file diff --git a/content/administration/data/tiered-storage.md b/content/administration/data/tiered-storage.md new file mode 100644 index 00000000..d3625768 --- /dev/null +++ b/content/administration/data/tiered-storage.md @@ -0,0 +1,179 @@ +--- +title: "Tiered Storage" +description: "Configure lifecycle-driven object transitions from RustFS to a remote storage tier." +--- + +RustFS tiered storage moves objects from local storage to a configured remote backend. This page explains the supported targets and shows how to add and maintain a RustFS tier in the Console. + +Tiering is asynchronous. RustFS keeps the object metadata locally, transfers the object data to the remote tier, and continues to serve S3 reads through the original bucket and object key. + +```mermaid +flowchart LR + A[Application] -->|S3 requests| H[Hot RustFS cluster] + H -->|Lifecycle transition| T[Remote tier bucket] + H -->|Read transitioned object| T +``` + +:::note[Tier names are not AWS storage classes] + +Lifecycle rules refer to a registered tier by its uppercase name, such as `COLDTIER`. Do not substitute AWS class labels such as `INTELLIGENT_TIERING`, `GLACIER`, or `DEEP_ARCHIVE` unless you have registered a RustFS tier with that exact valid name and verified the target behavior. + +::: + +## Supported backends + +The RustFS source defines warm-backend implementations for these target types: + +| Type | Configuration key | Typical target | +| --- | --- | --- | +| RustFS | `rustfs` | Another RustFS deployment | +| S3 | `s3` | Amazon S3 or an S3 endpoint supported by this backend | +| Wasabi | `wasabi` | Wasabi object storage | +| MinIO | `minio` | A MinIO deployment | +| Aliyun | `aliyun` | Alibaba Cloud Object Storage Service (OSS) | +| Tencent | `tencent` | Tencent Cloud Object Storage (COS) | +| Huaweicloud | `huaweicloud` | Huawei Cloud Object Storage Service (OBS) | +| Azure | `azure` | Azure Blob Storage | +| GCS | `gcs` | Google Cloud Storage | +| R2 | `r2` | Cloudflare R2 | + +Provider payloads and credential requirements differ. The complete workflow below uses the RustFS backend because the RustFS source includes an end-to-end hot-cluster-to-cold-cluster test for this path. + +## Before you begin + +Prepare the following: + +- A source RustFS deployment that stores the hot data. +- A separate target RustFS deployment and an existing target bucket. This example uses `my-bucket` on the target. +- Target credentials with permission to put, get, list, and delete objects in that bucket. +- Access to the source deployment's RustFS Console. + +Use TLS for both deployments in production. Restrict the target credentials to the dedicated tier bucket and prefix. + +## 1. Open Tiered Storage + +Sign in to the source deployment's RustFS Console. In the left navigation, select **Tiered Storage**, then select **Add Tier** in the upper-right corner. + +## 2. Select the target + +Select the target provider. This example uses **RustFS** to connect the source deployment to another RustFS deployment. + +## 3. Enter the target details + +Complete the form: + +| Field | Value | +| --- | --- | +| **Name (A-Z,0-9,_)** | Enter a unique uppercase tier name, such as `COLDTIER`. | +| **Endpoint** | Enter the target RustFS S3 endpoint. | +| **Access Key** | Enter the access key for the target deployment. | +| **Secret Key** | Enter the secret key for the target deployment. | +| **Bucket** | Enter the existing target bucket name, such as `my-bucket`. | +| **Prefix (Optional)** | Optionally enter a prefix dedicated to tiered objects. | +| **Region** | Optionally enter the target region, such as `us-east-1`. | + +Leave **Storage Class** at its default unless the target backend requires a different supported storage class. + +:::warning[Protect tier credentials] + +The form contains a secret key. Use credentials restricted to the target bucket and prefix. Do not expose the key in screenshots, tickets, or logs. + +::: + +## 4. Save the tier + +Select **Save**. RustFS validates the backend and probes it by writing, reading, and removing a small object. Saving fails when the endpoint, credentials, bucket permissions, or backend configuration cannot complete that probe. + +After the tier appears in the **Tiers** list, configure a transition rule in [Lifecycle Management](/administration/data/lifecycle-management). A registered tier does not move objects until a lifecycle rule references its name. + +## Manage tiers with `rc` + +Install and configure [`rc`](/operations/rc), then list the tiers registered on the source deployment: + +```bash +rc bucket lifecycle tier list local +``` + +Add a RustFS tier with the same settings described in the Console workflow: + +```bash +rc bucket lifecycle tier add rustfs COLDTIER local \ + --endpoint \ + --access-key \ + --secret-key \ + --bucket my-bucket \ + --region us-east-1 +``` + +Inspect the tier configuration and available statistics: + +```bash +rc bucket lifecycle tier info COLDTIER local +``` + +The remaining tier commands update credentials or remove a tier: + +```bash +rc bucket lifecycle tier edit COLDTIER local \ + --access-key \ + --secret-key +rc bucket lifecycle tier remove COLDTIER local +``` + +See [Lifecycle Management](/administration/data/lifecycle-management) to create transition rules, confirm transitions, and restore local copies. Run `rc bucket lifecycle tier --help` to inspect provider-specific options before changing a tier. + +## Monitor tier activity + +Use the tier statistics endpoint with a SigV4-signed request and `admin:ListTier` permission: + +```http +GET /rustfs/admin/v3/tier-stats?tier=COLDTIER HTTP/1.1 +Host: +``` + +Monitor transition failures together with source-cluster capacity and target-side availability. A configured tier adds the target service and network path to the read path for objects that do not have a restored local copy. + +## Change or remove a tier + +The Admin API exposes these mutation routes, all requiring `admin:SetTier`: + +| Operation | Route | +| --- | --- | +| Edit a tier | `POST /rustfs/admin/v3/tier/{tiername}` | +| Remove a tier | `DELETE /rustfs/admin/v3/tier/{tiername}` | +| Clear all tiers | `POST /rustfs/admin/v3/tier/clear` | + +Before editing or removing a tier: + +1. Disable lifecycle rules that reference the tier. +2. Confirm that no transition jobs are still using it. +3. Confirm that no source objects depend on data stored in its target bucket or prefix. +4. Back up the tier configuration and record the target location. + +Normal tier mutations check backend usage and protect non-empty targets. Do not use a `force` option to bypass those checks unless you have independently proved that every transitioned object remains recoverable. Removing configuration for an active tier can make transitioned objects unreadable from the source cluster. + +:::warning[Do not modify tier objects directly] + +Do not rename, overwrite, or delete generated objects in the target bucket. Manage source objects through the hot RustFS cluster so RustFS can keep local transition metadata and remote data consistent. + +::: + +## Admin API reference + +The current RustFS source registers these tier routes: + +| Method | Route | Permission | +| --- | --- | --- | +| `PUT` | `/rustfs/admin/v3/tier` | `admin:SetTier` | +| `POST` | `/rustfs/admin/v3/tier/{tiername}` | `admin:SetTier` | +| `DELETE` | `/rustfs/admin/v3/tier/{tiername}` | `admin:SetTier` | +| `POST` | `/rustfs/admin/v3/tier/clear` | `admin:SetTier` | +| `GET` | `/rustfs/admin/v3/tier` (list configurations) | `admin:ListTier` | +| `GET` | `/rustfs/admin/v3/tier/{tier}` (verify connectivity) | `admin:ListTier` | +| `GET` | `/rustfs/admin/v3/tier-stats` (read statistics) | `admin:ListTier` | + +All Admin API requests require SigV4 authentication. These routes are an administrative interface, not ordinary S3 bucket operations. + +## Next steps + +Review [lifecycle management](/administration/data/lifecycle-management) and configure [access policies](/security-compliance/iam/policies) for the administrators and service credentials used by tiered storage. \ No newline at end of file diff --git a/content/administration/iam/meta.json b/content/administration/iam/meta.json deleted file mode 100644 index 8987166c..00000000 --- a/content/administration/iam/meta.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "IAM", - "pages": [ - "[Overview](/administration/iam)", - "[Users, Groups, and Policies](/administration/iam/policies)", - "[Service Accounts and STS](/administration/iam/sts)", - "[External Identity (OIDC)](/administration/iam/oidc)", - "[Access Keys](/administration/iam/access-token)" - ] -} diff --git a/content/administration/iam/oidc.md b/content/administration/iam/oidc.md deleted file mode 100644 index ea95c027..00000000 --- a/content/administration/iam/oidc.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -title: "External Identity (OIDC)" -description: "Connect the RustFS Console to Keycloak, Authing, or any standard OpenID Connect provider for single sign-on." ---- - -RustFS supports standard OpenID Connect (OIDC) for Console login. Any standards-compliant provider works; this guide uses Keycloak and Authing as worked examples. The examples use the default RustFS provider id, `default`. - -## Integration Model - -RustFS expects a provider that offers issuer metadata (`.well-known/openid-configuration`), an authorization endpoint, a token endpoint, a JWKS (or another verifiable ID token signature path), and an authorization-code flow that returns an `id_token`. - -The browser login flow is: - -1. The user opens the RustFS OIDC authorize endpoint (`/rustfs/admin/v3/oidc/authorize/`). -2. RustFS creates `state`, `nonce`, and a PKCE S256 challenge, then redirects the browser to the provider. -3. The provider redirects back to `/rustfs/admin/v3/oidc/callback/` with `code` and `state`. -4. RustFS exchanges the code with `client_id`, `client_secret`, and the PKCE verifier. -5. RustFS validates the ID token signature, issuer, audience, expiry, and nonce. -6. RustFS maps ID token claim values to RustFS policy names and issues one-hour STS credentials for the Console. - -RustFS does not call the provider's authorization APIs for object or admin authorization. Authorization is handled entirely by RustFS policies after claims are mapped. - -## Claim Mapping - -RustFS reads `groups` or `roles` claims from the ID token and maps each value to a RustFS policy name. Keep claim values equal to policy names: - -| Claim value | RustFS policy | Purpose | -| --- | --- | --- | -| `consoleAdmin` | `consoleAdmin` | Full Console, admin, KMS, and S3 access. | -| `readwrite` | `readwrite` | S3 read/write access. | -| `readonly` | `readonly` | S3 read-only access. | -| `writeonly` | `writeonly` | S3 write-only access. | -| `diagnostics` | `diagnostics` | Diagnostic admin access. | - -If a login succeeds but no claim value matches a RustFS policy (and no group is mapped), the STS exchange is rejected. - -## RustFS Configuration - -Configure the provider and the public browser origin through environment variables, then restart RustFS: - -```bash -export RUSTFS_BROWSER_REDIRECT_URL="https://rustfs.example.com" - -export RUSTFS_IDENTITY_OPENID_ENABLE=on -export RUSTFS_IDENTITY_OPENID_CONFIG_URL="" -export RUSTFS_IDENTITY_OPENID_CLIENT_ID="" -export RUSTFS_IDENTITY_OPENID_CLIENT_SECRET="" -export RUSTFS_IDENTITY_OPENID_SCOPES="openid,profile,email" -export RUSTFS_IDENTITY_OPENID_REDIRECT_URI="https://rustfs.example.com/rustfs/admin/v3/oidc/callback/default" -export RUSTFS_IDENTITY_OPENID_REDIRECT_URI_DYNAMIC=off -export RUSTFS_IDENTITY_OPENID_DISPLAY_NAME="My IdP" -export RUSTFS_IDENTITY_OPENID_GROUPS_CLAIM="groups" -export RUSTFS_IDENTITY_OPENID_ROLES_CLAIM="roles" -export RUSTFS_IDENTITY_OPENID_EMAIL_CLAIM="email" -export RUSTFS_IDENTITY_OPENID_USERNAME_CLAIM="preferred_username" -``` - -For short-lived connectivity testing only, you may temporarily add: - -```bash -export RUSTFS_IDENTITY_OPENID_ROLE_POLICY="consoleAdmin" -``` - -:::warning - -Do not keep `role_policy=consoleAdmin` in production unless every user of this client should receive full Console access. Claim-to-policy mapping is the production authorization model. - -::: - -If the deployment manages configuration through compatible admin commands, the same keys can be set with `mc admin config set identity_openid enable=on config_url=... client_id=... client_secret=... scopes=... redirect_uri=... redirect_uri_dynamic=off display_name=... groups_claim=... roles_claim=... email_claim=... username_claim=...` followed by `mc admin service restart `. - -:::note - -`RUSTFS_BROWSER_REDIRECT_URL` is a process environment variable, not an `identity_openid` provider key. Configure it in the RustFS service environment even when the provider is stored through admin config. - -::: - -### Named Providers - -To register more than one provider (or use a provider id other than `default`), suffix the provider-specific environment variables with the provider id and register the matching callback URL at the IdP: - -```bash -export RUSTFS_IDENTITY_OPENID_ENABLE_keycloak=on -export RUSTFS_IDENTITY_OPENID_CONFIG_URL_keycloak="https://keycloak.example.com/realms/rustfs" -export RUSTFS_IDENTITY_OPENID_CLIENT_ID_keycloak="rustfs-console" -export RUSTFS_IDENTITY_OPENID_CLIENT_SECRET_keycloak="" -export RUSTFS_IDENTITY_OPENID_SCOPES_keycloak="openid,profile,email" -export RUSTFS_IDENTITY_OPENID_REDIRECT_URI_keycloak="https://rustfs.example.com/rustfs/admin/v3/oidc/callback/keycloak" -export RUSTFS_IDENTITY_OPENID_REDIRECT_URI_DYNAMIC_keycloak=off -export RUSTFS_IDENTITY_OPENID_DISPLAY_NAME_keycloak="Keycloak" -export RUSTFS_IDENTITY_OPENID_GROUPS_CLAIM_keycloak="groups" -``` - -`RUSTFS_BROWSER_REDIRECT_URL` remains global and is not suffixed per provider. - -### Redirect URL Priority - -RustFS builds browser-facing URLs with this priority: - -1. Provider `redirect_uri`, when configured, is used for the OIDC callback URL sent to the IdP. -2. `RUSTFS_BROWSER_REDIRECT_URL`, when configured, is used as the public origin for OIDC callback generation when no provider `redirect_uri` exists, and for Console success redirects and logout fallback redirects. -3. Request headers are used only when provider dynamic redirects are enabled and no browser redirect URL is configured. - -For reverse-proxy or load-balancer deployments, set `RUSTFS_BROWSER_REDIRECT_URL` to avoid depending on `Host` and `X-Forwarded-Proto`. OIDC authorize and callback requests must reach the same RustFS node, because in-flight OIDC `state` is local to the node — configure session affinity on the load balancer. - -## Example: Keycloak - -Example values: realm `rustfs`, issuer `https://keycloak.example.com/realms/rustfs`, client id `rustfs-console`, callback `https://rustfs.example.com/rustfs/admin/v3/oidc/callback/default`. - -1. Create or select the `rustfs` realm and verify discovery: - - ```bash - curl -fsS "https://keycloak.example.com/realms/rustfs/.well-known/openid-configuration" \ - | jq '.issuer,.authorization_endpoint,.token_endpoint,.jwks_uri' - ``` - -2. Create a client: `Client type` = `OpenID Connect`, `Client ID` = `rustfs-console`, enable `Client authentication` and `Standard flow`, disable `Implicit flow`, `Direct access grants`, and `Service accounts roles`. Set `Valid redirect URIs` to the exact RustFS callback URL, `Web origins` to `https://rustfs.example.com`, and PKCE Code Challenge Method to `S256`. Copy the client secret from `Credentials`. -3. Map groups to policies: create Keycloak groups named after RustFS policies (e.g. `consoleAdmin`, `readonly`), add users, and add a `Group Membership` mapper to the client scope with `Token Claim Name` = `groups`, `Full group path` = `Off`, `Add to ID token` = `On`, `Multivalued` = `On`. -4. Configure RustFS with `RUSTFS_IDENTITY_OPENID_CONFIG_URL="https://keycloak.example.com/realms/rustfs"` and the client id/secret as shown above, then restart. - -:::note - -Keep `Full group path` disabled. RustFS policy names cannot contain `/`, so `/consoleAdmin` will not map to the `consoleAdmin` policy. If you use Keycloak roles instead of groups, emit a flat top-level `roles` claim (via a `User Realm Role` or `User Client Role` mapper) and set `RUSTFS_IDENTITY_OPENID_ROLES_CLAIM=roles` — RustFS does not parse Keycloak's nested `realm_access.roles` claim. RustFS submits the client secret in the token request body, so do not disable `client_secret_post`. - -::: - -## Example: Authing - -Example values: application domain `https://example.authing.cn`, issuer `https://example.authing.cn/oidc`, App ID as `client_id`, App Secret as `client_secret`. - -1. Create a self-hosted application named `RustFS Console`; record the App ID, App Secret, issuer, and discovery URL. Authing deployments use different issuer paths (`/oidc` or `/oauth/oidc`) — always copy the issuer from the Authing console. -2. Protocol settings: Protocol = OpenID Connect, Grant type = Authorization Code, Response type = `code`, token endpoint authentication = `client_secret_post`, PKCE = allow or require `S256`, ID token signing algorithm = `RS256` recommended. -3. Register the exact callback URL `https://rustfs.example.com/rustfs/admin/v3/oidc/callback/default`. -4. Assign users roles named after RustFS policies and confirm the ID token contains, for example: - - ```json - { - "roles": ["consoleAdmin"] - } - ``` - -5. Configure RustFS with `RUSTFS_IDENTITY_OPENID_SCOPES="openid,profile,email,roles"` and `RUSTFS_IDENTITY_OPENID_ROLES_CLAIM="roles"`, then restart. - -## Validation - -Verify provider discovery, then check that the provider is visible to RustFS: - -```bash -curl -fsS "https://rustfs.example.com/rustfs/admin/v3/oidc/providers" | jq -``` - -Test the browser flow by opening: - -```text -https://rustfs.example.com/rustfs/admin/v3/oidc/authorize/default -``` - -Expected result: redirect to the IdP, sign in, redirect back to `/rustfs/admin/v3/oidc/callback/default?code=...&state=...`, RustFS validates the ID token, issues STS credentials, and the browser lands on the Console with the mapped permissions. - -## Troubleshooting - -| Symptom | Common cause | Fix | -| --- | --- | --- | -| `/oidc/providers` does not show the provider | Provider did not load, or RustFS was not restarted | Check environment variables and restart RustFS. | -| IdP reports a redirect mismatch | Registered redirect URI differs from the RustFS callback URL | Use the exact callback URL including the provider id. | -| Callback reports missing `code` or `state` | Proxy dropped the query string | Preserve the full callback URL and query string. | -| Token exchange fails | Wrong client secret or token auth method | Confirm the client is confidential and accepts `client_secret_post`. | -| RustFS reports no `id_token` | Missing `openid` scope or non-OIDC flow | Include `openid` and use the authorization code flow. | -| ID token verification fails | Issuer, audience, signing algorithm, or JWKS mismatch | Compare discovery metadata with the RustFS config; prefer `RS256`. | -| Login succeeds but access is denied | No claim value matched a RustFS policy | Emit `groups` or `roles` as a flat ID token claim matching policy names. | -| Console redirects to an internal host | Missing `RUSTFS_BROWSER_REDIRECT_URL` | Set it to the public browser origin. | -| Invalid or expired OIDC state | Callback reached a different RustFS node | Configure load-balancer session affinity for authorize and callback. | - -## Production Checklist - -- The IdP and RustFS use HTTPS. -- The registered redirect URI is the exact callback URL, not a wildcard. -- `RUSTFS_BROWSER_REDIRECT_URL` is set to the public RustFS browser origin. -- `RUSTFS_IDENTITY_OPENID_REDIRECT_URI` matches the registered callback URL. -- PKCE S256 is enabled or required. -- Users receive `groups` or `roles` claims that match RustFS policy names. -- `role_policy=consoleAdmin` is not used as a permanent production shortcut. -- The load balancer preserves query strings and pins authorize/callback requests to one node. diff --git a/content/administration/index.md b/content/administration/index.md new file mode 100644 index 00000000..2c059b42 --- /dev/null +++ b/content/administration/index.md @@ -0,0 +1,16 @@ +--- +title: "Administration" +description: "Administer RustFS data, access protocols, and browser-based management." +--- + +Use this section to manage RustFS through the Console, administer buckets and objects, and configure access protocols. + +## Administration areas + +- [Console](./console/index.md) covers browser-based administration and sign-in methods. +- [Data Management](./data/object/object-lock.md) covers buckets, objects, lifecycle behavior, and data protection features. +- [Protocol Support](./protocols/s3.md) covers S3, WebDAV, FTPS, and SFTP access. +- [CORS Configuration](./cors/index.md) covers cross-origin access to RustFS services. +- [Virtual-Host Access](/integration/virtual) covers domain-based S3 addressing. + +For identity, encryption, transport security, and auditing, continue with [Security & Compliance](/security-compliance). diff --git a/content/administration/meta.json b/content/administration/meta.json new file mode 100644 index 00000000..18fcca57 --- /dev/null +++ b/content/administration/meta.json @@ -0,0 +1,11 @@ +{ + "title": "Administration", + "defaultOpen": true, + "pages": [ + "[Console](/administration/console)", + "data", + "protocols", + "cors", + "[Virtual-Host Access](/integration/virtual)" + ] +} \ No newline at end of file diff --git a/content/administration/protocols/ftps.md b/content/administration/protocols/ftps.md new file mode 100644 index 00000000..e7031a02 --- /dev/null +++ b/content/administration/protocols/ftps.md @@ -0,0 +1,246 @@ +--- +title: "FTP(S)" +description: "Configure the RustFS FTP or FTPS gateway and manage buckets and objects with FTP clients." +--- + +RustFS includes an FTP gateway that exposes buckets and objects to standard FTP clients. You can run it as unencrypted FTP for isolated local testing or as explicit FTP over TLS (FTPS) for encrypted connections. RustFS authenticates each session against Identity and Access Management (IAM) and applies the user's S3 policies to storage operations. + +FTP and FTPS support is compiled into the standard RustFS binary, but both listeners are disabled at runtime by default. Enable only the listener you intend to use. + +## Overview + +The gateway maps FTP paths to RustFS resources: + +| FTP path | RustFS resource | +| --- | --- | +| `/` | All buckets visible to the authenticated user | +| `/my-bucket/` | The `my-bucket` bucket | +| `/my-bucket/hello.txt` | The `hello.txt` object in `my-bucket` | + +The following FTP operations are supported: + +| FTP command | Operation | +| --- | --- | +| `LIST` | List buckets at the root or objects and prefixes in a bucket | +| `MKD` | Create a bucket | +| `CWD` | Enter a bucket | +| `STOR` | Upload an object | +| `RETR` | Download an object | +| `DELE` | Delete an object | +| `RMD` | Recursively delete a bucket and its objects | + +RustFS does not currently support FTP rename operations or uploads that append to an existing object. Because S3 storage has no native working-directory or POSIX directory model, some client-specific filesystem operations may not behave like a traditional FTP server. + +Enter a RustFS access key as the FTP username and its secret key as the password. Invalid usernames and passwords both return `530 Not logged in`. After login, RustFS checks the IAM user's S3 permissions for each operation. + +:::danger[Plain FTP exposes credentials] + +FTP sends credentials and data without encryption. Bind plain FTP to a loopback or isolated test network only. Use FTPS for every remote or production connection. + +::: + +:::warning[Bucket deletion is recursive] + +The current `RMD` implementation deletes the objects in the target bucket before deleting the bucket. Confirm the path before running `rmdir` in an FTP client. + +::: + +## Configuration + +FTP and FTPS use separate listeners and environment variables. + +### FTP variables + +| Variable | Description | Default | +| --- | --- | --- | +| `RUSTFS_FTP_ENABLE` | Enables the unencrypted FTP listener. | `false` | +| `RUSTFS_FTP_ADDRESS` | Bind address for FTP control connections. | `0.0.0.0:8021` | +| `RUSTFS_FTP_PASSIVE_PORTS` | Inclusive passive data port range in `start-end` format. | `40000-50000` | +| `RUSTFS_FTP_EXTERNAL_IP` | Public IP or hostname advertised to passive clients when RustFS is behind NAT. | Not set | + +### FTPS variables + +| Variable | Description | Default | +| --- | --- | --- | +| `RUSTFS_FTPS_ENABLE` | Enables the explicit FTPS listener. | `false` | +| `RUSTFS_FTPS_ADDRESS` | Bind address for FTPS control connections. | `0.0.0.0:8022` | +| `RUSTFS_FTPS_TLS_ENABLED` | Enables TLS on the FTPS listener. Keep this enabled for FTPS. | `true` | +| `RUSTFS_FTPS_CERTS_DIR` | Directory containing the FTPS certificate and private key. Required for FTPS. | Not set | +| `RUSTFS_FTPS_PASSIVE_PORTS` | Inclusive passive data port range in `start-end` format. | `40000-50000` | +| `RUSTFS_FTPS_EXTERNAL_IP` | Public IP or hostname advertised to passive clients when RustFS is behind NAT. | Not set | + +The standard RustFS build enables the `ftps` compile-time feature, which provides both FTP and FTPS. If you build RustFS with `--no-default-features`, include the feature explicitly: + +```bash +cargo build --release --features ftps +``` + +### Local testing with FTP + +Start an unencrypted FTP listener on the loopback interface: + +```bash +export RUSTFS_FTP_ENABLE=true +export RUSTFS_FTP_ADDRESS=127.0.0.1:8021 +export RUSTFS_FTP_PASSIVE_PORTS=40000-40010 +export RUSTFS_ACCESS_KEY= +export RUSTFS_SECRET_KEY= + +rustfs /path/to/data +``` + +Binding to `127.0.0.1` prevents remote hosts from connecting to the unencrypted listener. + +### Prepare a test certificate + +FTPS expects `rustfs_cert.pem` and `rustfs_key.pem` in the certificate directory. For local testing, create a short-lived self-signed certificate: + +```bash +mkdir -p /path/to/ftps-certs + +openssl req -x509 -newkey rsa:2048 -nodes \ + -keyout /path/to/ftps-certs/rustfs_key.pem \ + -out /path/to/ftps-certs/rustfs_cert.pem \ + -days 7 \ + -subj "/CN=localhost" \ + -addext "subjectAltName=DNS:localhost,IP:127.0.0.1" +``` + +Use a certificate issued by a trusted certificate authority in production. Protect `rustfs_key.pem` from unauthorized access and ensure the certificate subject alternative names match the host clients use. + +### Start FTPS + +Configure an explicit FTPS listener: + +```bash +export RUSTFS_FTPS_ENABLE=true +export RUSTFS_FTPS_ADDRESS=0.0.0.0:8022 +export RUSTFS_FTPS_TLS_ENABLED=true +export RUSTFS_FTPS_CERTS_DIR=/path/to/ftps-certs +export RUSTFS_FTPS_PASSIVE_PORTS=40000-50000 + +rustfs /path/to/data +``` + +RustFS requires TLS on both the FTPS control and data channels. Configure clients for **explicit FTP over TLS**, sometimes labeled **FTPES** or **Require explicit FTP over TLS**. Implicit FTPS is not the mode implemented by this listener. + +If the certificate directory is missing, does not exist, or does not contain a usable certificate and key, FTPS initialization fails. See [Configure TLS](/integration/tls-configured) for general certificate guidance. + +### Configure passive connections + +FTP uses the control port for commands and a separate data connection for listings and file transfers. For passive mode: + +1. Allow inbound TCP traffic to the configured control port. +2. Allow inbound TCP traffic to every port in the configured passive range. +3. Set `RUSTFS_FTP_EXTERNAL_IP` or `RUSTFS_FTPS_EXTERNAL_IP` when clients connect through NAT or a load balancer. + +For example, an FTPS server behind NAT can advertise its public hostname: + +```bash +export RUSTFS_FTPS_EXTERNAL_IP=storage.example.com +export RUSTFS_FTPS_PASSIVE_PORTS=40000-40100 +``` + +Use the corresponding FTP-prefixed variables for the plain FTP listener. RustFS supports both active and passive transfer modes, but passive mode is generally easier to operate through client firewalls. + +## Usage + +The examples use [`lftp`](https://lftp.yar.ru/) and the canonical `my-bucket` and `hello.txt` names. The IAM user must have the S3 permissions required for each operation. + +### Connect with FTP + +Use plain FTP only with the loopback test listener: + +```bash +lftp -u ftp://127.0.0.1:8021 +``` + +Enter the secret key when `lftp` prompts for a password. + +### Connect with FTPS + +Connect to the explicit FTPS listener and require encryption for control and data connections: + +```bash +lftp -u -e ' + set ftp:ssl-force true; + set ftp:ssl-protect-data true; + open ftp://storage.example.com:8022 +' +``` + +Enter the secret key when prompted. Keep certificate verification enabled in production. + +For the self-signed local test certificate only, connect to `localhost` and disable certificate verification for that session: + +```bash +lftp -u -e ' + set ftp:ssl-force true; + set ftp:ssl-protect-data true; + set ssl:verify-certificate no; + open ftp://localhost:8022 +' +``` + +:::warning + +Do not disable certificate verification in production. Install the issuing CA in the client trust store instead. + +::: + +### List buckets and create a bucket + +At the `lftp` prompt, list the visible buckets and create `my-bucket`: + +```text +lftp> cls -1 / +lftp> mkdir my-bucket +lftp> cd my-bucket +``` + +Bucket names must follow the same naming rules as buckets created through the S3 API. + +### Upload and download an object + +Upload `/path/to/hello.txt`, list the bucket, and download the object: + +```text +lftp> put /path/to/hello.txt -o hello.txt +lftp> ls +lftp> get hello.txt -o hello.txt +``` + +The upload replaces an object with the same key. Appending to an existing object is not supported. + +### Delete an object and bucket + +Delete the object, return to the root, and delete the bucket: + +```text +lftp> rm hello.txt +lftp> cd / +lftp> rmdir my-bucket +``` + +Remember that `rmdir` recursively removes remaining objects from the bucket before deleting it. + +### Connect other clients + +Use these settings in graphical FTP clients such as FileZilla or Cyberduck: + +| Setting | FTP test listener | FTPS listener | +| --- | --- | --- | +| Protocol | FTP | FTP over TLS (explicit) | +| Host | `127.0.0.1` | Your FTPS hostname | +| Port | `8021` | `8022` | +| Username | RustFS access key | RustFS access key | +| Password | RustFS secret key | RustFS secret key | +| Transfer mode | Passive | Passive | + +If login succeeds but directory listings or transfers time out, check the passive port firewall rules and external IP setting first. + +## Next steps + +- [Manage credentials](/operations/credentials) +- [Configure TLS](/integration/tls-configured) +- [Check service status](/operations/status-check) \ No newline at end of file diff --git a/content/administration/protocols/meta.json b/content/administration/protocols/meta.json new file mode 100644 index 00000000..f1395739 --- /dev/null +++ b/content/administration/protocols/meta.json @@ -0,0 +1,10 @@ +{ + "title": "Protocol Support", + "pages": [ + "s3", + "webdav", + "ftps", + "sftp", + "[MCP Server](/developer/mcp)" + ] +} \ No newline at end of file diff --git a/content/administration/protocols/s3.md b/content/administration/protocols/s3.md new file mode 100644 index 00000000..0148cd28 --- /dev/null +++ b/content/administration/protocols/s3.md @@ -0,0 +1,200 @@ +--- +title: "S3" +description: "Configure and use the RustFS S3-compatible API with AWS CLI, AWS SDKs, and other S3 clients." +--- + +RustFS exposes an S3-compatible REST API for common object-storage workloads. It supports AWS Signature Version 4 (SigV4) and works with AWS CLI, AWS SDKs, and other S3 clients when you configure the RustFS endpoint, region, credentials, and addressing style. + +## Overview + +S3 organizes data into buckets and objects. With the default path-style addressing, a RustFS URL maps to storage as follows: + +```text +http://localhost:9000/my-bucket/path/to/hello.txt + | bucket | |---- object key ----| +``` + +RustFS covers the common S3 data plane used by applications, backup tools, and SDKs: + +| Area | Supported workflows | +| --- | --- | +| Buckets | Create, delete, list, inspect, and retrieve location | +| Objects | Put, get, head, copy, delete, multi-delete, range reads, conditional requests, and user metadata | +| Listing | `ListObjects`, `ListObjectsV2`, prefixes, delimiters, markers, and pagination | +| Large objects | Create, upload, copy, list, complete, and abort multipart uploads | +| Data management | Versioning, lifecycle rules, bucket and object tags, checksums, and object lock | +| Access control | IAM credentials and policies, bucket policies, public access block, and presigned GET/PUT URLs | +| Integration | CORS, bucket notifications, replication configuration, and server-side encryption workflows | + +Authenticated clients sign requests with an access key and secret key. Anonymous requests are evaluated against the applicable bucket policy. + +:::note[Compatibility scope] + +RustFS provides broad S3 API compatibility for supported features, but it is not identical to every AWS S3 or MinIO API. Review [Known compatibility differences](#known-compatibility-differences) before depending on advanced or vendor-specific behavior, and validate it against your target RustFS release. + +::: + +## Configuration + +The S3 API is enabled on the main RustFS listener. It does not require a separate protocol switch. + +| Variable | Purpose | Default | +| --- | --- | --- | +| `RUSTFS_ADDRESS` | S3 API bind address | `:9000` | +| `RUSTFS_REGION` | Region used for request signing and region-aware clients | `us-east-1` | +| `RUSTFS_ACCESS_KEY` | Root access key | Installation-specific | +| `RUSTFS_SECRET_KEY` | Root secret key | Installation-specific | +| `RUSTFS_SERVER_DOMAINS` | Comma-separated domains for virtual-hosted-style requests | Not set | +| `RUSTFS_TLS_PATH` | Directory containing `rustfs_cert.pem` and `rustfs_key.pem` | Not set | + +Use IAM users or service accounts instead of root credentials for applications, and grant only the required S3 actions. + +### Path-style addressing + +Path-style addressing is the default and requires no DNS configuration. The bucket name is the first component of the request path: + +```text +http://localhost:9000/my-bucket/hello.txt +``` + +Configure clients with: + +- Endpoint: `http://localhost:9000` +- Region: `us-east-1` +- Access key: `` +- Secret key: `` +- Addressing style: path-style + +Some clients default to virtual-hosted-style requests. Enable their path-style option, such as `force_path_style=true` in AWS SDK configuration or `s3_use_path_style = true` in Terraform. + +### Virtual-hosted-style addressing + +Virtual-hosted-style addressing places the bucket name in the hostname. Set `RUSTFS_SERVER_DOMAINS` to each base domain that RustFS should recognize: + +```bash +export RUSTFS_SERVER_DOMAINS="s3.example.com" +``` + +The same object is then addressed as: + +```text +https://my-bucket.s3.example.com/hello.txt +``` + +Configure wildcard DNS, such as `*.s3.example.com`, to resolve to the RustFS endpoint. For HTTPS, the certificate must also cover the bucket hostnames. Bucket names containing dots may require explicit certificate names because a single-label wildcard does not span multiple labels. + +:::warning[Configure the server domain] + +Do not send virtual-hosted-style requests unless `RUSTFS_SERVER_DOMAINS` is configured. RustFS otherwise treats requests as path-style and cannot derive the bucket from the hostname. + +::: + +### TLS and network access + +For production, set `RUSTFS_TLS_PATH` to a directory containing `rustfs_cert.pem` and `rustfs_key.pem`, then use an `https://` endpoint. Ensure clients trust the issuing certificate authority. + +Allow the configured S3 API port through host firewalls and load balancers. The default is TCP port `9000`; this listener also carries RustFS administrative and internode traffic, so do not expose it anonymously without appropriate network and IAM controls. + +## Usage + +The following examples use AWS CLI with the canonical local endpoint. Install and configure [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) before continuing. + +### Configure AWS CLI + +```bash +aws configure +``` + +Enter these values when prompted: + +```text +AWS Access Key ID [None]: +AWS Secret Access Key [None]: +Default region name [None]: us-east-1 +Default output format [None]: json +``` + +Pass the RustFS endpoint to every command. The custom endpoint keeps these examples on RustFS instead of AWS S3. + +```bash +export RUSTFS_ENDPOINT="http://localhost:9000" +``` + +### Create and list buckets + +```bash +aws --endpoint-url "$RUSTFS_ENDPOINT" s3api create-bucket \ + --bucket my-bucket + +aws --endpoint-url "$RUSTFS_ENDPOINT" s3api list-buckets +``` + +### Upload and list objects + +```bash +printf 'Hello from RustFS\n' > hello.txt + +aws --endpoint-url "$RUSTFS_ENDPOINT" s3 cp \ + hello.txt s3://my-bucket/path/to/hello.txt + +aws --endpoint-url "$RUSTFS_ENDPOINT" s3 ls \ + s3://my-bucket/path/to/ +``` + +AWS CLI automatically uses multipart upload when its transfer configuration selects it for a large object. RustFS supports the standard multipart create, upload-part, complete, list, and abort workflows. + +### Download and inspect an object + +```bash +aws --endpoint-url "$RUSTFS_ENDPOINT" s3 cp \ + s3://my-bucket/path/to/hello.txt ./downloaded-hello.txt + +aws --endpoint-url "$RUSTFS_ENDPOINT" s3api head-object \ + --bucket my-bucket \ + --key path/to/hello.txt +``` + +### Create a presigned URL + +Generate a time-limited URL that can download an object without exposing credentials: + +```bash +aws --endpoint-url "$RUSTFS_ENDPOINT" s3 presign \ + s3://my-bucket/path/to/hello.txt \ + --expires-in 3600 +``` + +The hostname, scheme, port, region, and object path used by the recipient must match the values used to sign the URL. Use the externally reachable endpoint when generating URLs for another machine. + +### Delete objects and buckets + +```bash +aws --endpoint-url "$RUSTFS_ENDPOINT" s3 rm \ + s3://my-bucket/path/to/hello.txt + +aws --endpoint-url "$RUSTFS_ENDPOINT" s3api delete-bucket \ + --bucket my-bucket +``` + +An S3 bucket must be empty before it can be deleted. + +## Known compatibility differences + +Current RustFS compatibility tests cover the common workflows described above. The following areas are not equivalent to AWS S3 or MinIO: + +- Bucket access logging and bucket ownership controls are planned rather than complete. +- ACL authorization is intentionally unsupported. Canned ACL headers have partial compatibility, while XML grant policies return `NotImplemented`. Prefer IAM and bucket policies. +- POST Object form uploads are implemented, but checksum handling for that workflow is not complete. +- Some multipart upload listing and part-lookup edge cases are outside the default compatibility gate. +- S3 Select currently accepts only uncompressed input. +- Access Point and Outposts copy-source forms are not implemented. +- MinIO administrative APIs are a separate compatibility surface and should not be inferred from S3 data-plane support. + +Client behavior can also differ when a tool assumes AWS-specific services, storage classes, account ownership controls, or endpoint discovery. Always set the RustFS endpoint explicitly and test the exact operations your application uses. + +## Next steps + +- Follow the complete [AWS CLI example](/developer/examples/aws-cli). +- Choose an [S3 SDK](/developer/sdk) for application integration. +- Configure [credentials and access policies](/operations/credentials). +- Enable [TLS](/integration/tls-configured) before exposing the endpoint outside a trusted network. diff --git a/content/administration/protocols/sftp.md b/content/administration/protocols/sftp.md new file mode 100644 index 00000000..3579364f --- /dev/null +++ b/content/administration/protocols/sftp.md @@ -0,0 +1,181 @@ +--- +title: "SFTP" +description: "Build, configure, and use the RustFS SFTP gateway to access buckets and objects with SFTP clients." +--- + +RustFS includes an SSH File Transfer Protocol (SFTP) gateway that exposes buckets and objects to SFTP clients over an encrypted SSH connection. You can list and create buckets and upload, download, rename, or delete objects while RustFS enforces the permissions of the authenticated Identity and Access Management (IAM) user. + +SFTP is an optional compile-time feature and is not included in the default RustFS build. Build RustFS with the `sftp` or `full` feature before enabling the listener. The listener is disabled at runtime by default. + +## Overview + +The gateway maps SFTP paths to RustFS resources: + +| SFTP path | RustFS resource | +| --- | --- | +| `/` | All buckets visible to the authenticated user | +| `/my-bucket/` | The `my-bucket` bucket | +| `/my-bucket/hello.txt` | The `hello.txt` object in `my-bucket` | +| `/my-bucket/docs/hello.txt` | The `docs/hello.txt` object in `my-bucket` | + +Creating or removing a directory directly below `/` creates or removes a bucket. Directories below a bucket map to object key prefixes and do not exist as independent filesystem directories. + +RustFS supports the following operations through the SFTP gateway: + +| SFTP operation | RustFS operation | +| --- | --- | +| List `/` | List visible buckets | +| List a bucket or prefix | List objects and prefixes | +| `mkdir` at `/` | Create a bucket | +| `put` | Upload an object | +| `get` | Download an object | +| `rename` | Copy an object and then delete the source | +| `rm` | Delete an object | +| `rmdir` at `/` | Delete an empty bucket | + +Enter a RustFS access key as the SFTP username and its secret key as the password. RustFS checks the IAM user's S3 policies for every operation. + +:::warning[Password authentication only] + +The SFTP gateway does not support client public-key authentication or anonymous access. SSH host keys identify the server; they do not authenticate clients. Do not configure an `authorized_keys` file for RustFS SFTP. + +::: + +## Configuration + +### Build with SFTP support + +Build RustFS with the SFTP feature: + +```bash +cargo build --release --features sftp +``` + +To enable all optional RustFS features, including SFTP, use `--features full` instead. Setting `RUSTFS_SFTP_ENABLE=true` has no effect in a binary built without the `sftp` feature. + +### SFTP variables + +| Variable | Description | Default | +| --- | --- | --- | +| `RUSTFS_SFTP_ENABLE` | Enables the SFTP listener. | `false` | +| `RUSTFS_SFTP_ADDRESS` | Bind address for SFTP connections. | `0.0.0.0:2222` | +| `RUSTFS_SFTP_HOST_KEY_DIR` | Directory containing at least one unencrypted SSH private host key. Required when SFTP is enabled. | Not set | +| `RUSTFS_SFTP_READ_ONLY` | Rejects operations that modify buckets or objects. | `false` | +| `RUSTFS_SFTP_IDLE_TIMEOUT` | SSH inactivity timeout in seconds. Must be greater than zero. | `600` | +| `RUSTFS_SFTP_PART_SIZE` | Multipart upload part size in bytes. | `16777216` (16 MiB) | + +The default port `2222` avoids the privileged SSH port `22`. The address can use IPv4 or IPv6 syntax, for example `127.0.0.1:2222` or `[::]:2222`. + +### Prepare a host key + +Create a persistent host-key directory and generate an Ed25519 host key: + +```bash +mkdir -p /path/to/sftp-keys +ssh-keygen -t ed25519 \ + -f /path/to/sftp-keys/ssh_host_ed25519_key \ + -N "" +chmod 600 /path/to/sftp-keys/ssh_host_ed25519_key* +``` + +The host key must not use a passphrase. RustFS also accepts decodable ECDSA and RSA private host keys. + +On Unix, every regular file in the host-key directory must be accessible only by its owner. The `ssh-keygen` command also creates a `.pub` file; either keep it with owner-only permissions as shown or remove it because RustFS does not read it. RustFS refuses to start if the directory is missing, contains no usable private key, or contains a regular file with group or other permission bits set. + +:::warning[Protect the host key] + +Keep the same host key across restarts so clients can verify the server identity. Restrict access to the account that runs RustFS and back up the key securely. A changed host key causes clients to display a possible man-in-the-middle warning. + +::: + +### Start the SFTP listener + +The following example starts SFTP on the loopback interface for local testing: + +```bash +export RUSTFS_SFTP_ENABLE=true +export RUSTFS_SFTP_ADDRESS=127.0.0.1:2222 +export RUSTFS_SFTP_HOST_KEY_DIR=/path/to/sftp-keys +export RUSTFS_ACCESS_KEY= +export RUSTFS_SECRET_KEY= + +rustfs /path/to/data +``` + +For remote connections, bind to an appropriate network interface and allow inbound TCP traffic to the configured SFTP port. This listener is separate from the S3 API and Console listeners. + +## Usage + +The following examples use the OpenSSH `sftp` client, the canonical `my-bucket` and `hello.txt` names, and a local listener on port `2222`. + +### Connect + +```bash +sftp -P 2222 @127.0.0.1 +``` + +Enter the RustFS secret key when prompted for the password. On the first connection, verify the displayed host-key fingerprint before accepting it. + +### List and create buckets + +At the `sftp` prompt, list the buckets visible to the IAM user and create `my-bucket`: + +```text +sftp> ls / +sftp> mkdir /my-bucket +sftp> cd /my-bucket +``` + +Bucket names must follow the same naming rules as buckets created through the S3 API. Files cannot be created directly under `/`. + +### Upload and download an object + +Upload `/path/to/hello.txt`, list the bucket, and download the object: + +```text +sftp> put /path/to/hello.txt /my-bucket/hello.txt +sftp> ls /my-bucket +sftp> get /my-bucket/hello.txt hello.txt +``` + +Uploads must be sequential from the beginning of the file. Resume, append, in-place edits, and segmented multi-connection uploads of one object are not supported. + +### Rename an object + +```text +sftp> rename /my-bucket/hello.txt /my-bucket/greeting.txt +``` + +RustFS implements rename as a server-side copy followed by deletion of the source. The operation is not atomic, and renaming a bucket is not supported. The IAM user needs permission to read and delete the source and to write the destination. + +### Delete an object and bucket + +Delete the object and then remove the empty bucket: + +```text +sftp> rm /my-bucket/greeting.txt +sftp> rmdir /my-bucket +``` + +RustFS does not recursively delete a non-empty bucket through SFTP. + +### Connect a desktop client + +Use these settings in graphical clients such as FileZilla, Cyberduck, or WinSCP: + +| Setting | Value | +| --- | --- | +| Protocol | SFTP (SSH File Transfer Protocol) | +| Host | Your RustFS SFTP hostname | +| Port | `2222`, or the port in `RUSTFS_SFTP_ADDRESS` | +| Username | RustFS access key | +| Password | RustFS secret key | +| Authentication | Password | + +Configure the client for whole-file, single-connection uploads. Symbolic links and POSIX ownership, permission, or timestamp changes are not supported because the gateway maps SFTP operations to object storage. + +## Next steps + +- [Manage credentials](/operations/credentials) +- [Check service status](/operations/status-check) +- [Configure lifecycle management](/administration/data/lifecycle-management) \ No newline at end of file diff --git a/content/administration/protocols/webdav.md b/content/administration/protocols/webdav.md new file mode 100644 index 00000000..ab6a80ff --- /dev/null +++ b/content/administration/protocols/webdav.md @@ -0,0 +1,208 @@ +--- +title: "WebDAV" +description: "Configure the RustFS WebDAV gateway and access buckets and objects with WebDAV clients." +--- + +RustFS includes a Web Distributed Authoring and Versioning (WebDAV) gateway that exposes buckets and objects to WebDAV clients over HTTP or HTTPS. You can browse buckets, create collections, and upload, download, rename, or delete objects while RustFS enforces the permissions of the authenticated Identity and Access Management (IAM) user. + +WebDAV support is compiled into the standard RustFS binary, but the gateway is disabled at runtime by default. You must enable and configure it before connecting a client. + +## Overview + +The gateway maps WebDAV paths to RustFS resources: + +| WebDAV path | RustFS resource | +| --- | --- | +| `/` | All buckets visible to the authenticated user | +| `/my-bucket/` | The `my-bucket` bucket | +| `/my-bucket/hello.txt` | The `hello.txt` object in `my-bucket` | +| `/my-bucket/docs/hello.txt` | The `docs/hello.txt` object in `my-bucket` | + +RustFS supports the following operations through the gateway: + +| Method | Operation | +| --- | --- | +| `PROPFIND` | List buckets or objects and read metadata | +| `MKCOL` | Create a bucket or a directory prefix | +| `PUT` | Upload an object | +| `GET` | Download an object | +| `HEAD` | Read object metadata | +| `MOVE` | Rename or move an object or directory | +| `DELETE` | Delete an object, directory, or bucket | + +Use `PROPFIND`, not `GET`, to list a collection. The current gateway returns `405 Method Not Allowed` for `GET` requests to directories. + +WebDAV uses HTTP Basic authentication. Enter a RustFS access key as the username and its secret key as the password. The gateway authenticates the credentials against RustFS IAM and applies the user's S3 policies to each operation. + +:::warning[Protect credentials] + +Basic authentication does not encrypt credentials. Disable TLS only for isolated local testing. Use HTTPS for every remote or production connection. + +::: + +## Configuration + +Configure the gateway with environment variables before starting RustFS: + +| Variable | Description | Default | +| --- | --- | --- | +| `RUSTFS_WEBDAV_ENABLE` | Enables the WebDAV gateway. | `false` | +| `RUSTFS_WEBDAV_ADDRESS` | Bind address for WebDAV connections. | `0.0.0.0:8080` | +| `RUSTFS_WEBDAV_TLS_ENABLED` | Enables TLS for the WebDAV listener. | `true` | +| `RUSTFS_WEBDAV_CERTS_DIR` | Certificate directory used by the RustFS TLS runtime. Required when TLS is enabled. | Not set | +| `RUSTFS_WEBDAV_MAX_BODY_SIZE` | Maximum request body size in bytes. Must be greater than zero. | `5368709120` (5 GiB) | + +The standard RustFS build enables the WebDAV compile-time feature. If you build RustFS with `--no-default-features`, include the feature explicitly: + +```bash +cargo build --release --features webdav +``` + +### Local testing without TLS + +The following example starts RustFS with an HTTP WebDAV listener on port `8080`: + +```bash +export RUSTFS_WEBDAV_ENABLE=true +export RUSTFS_WEBDAV_ADDRESS=127.0.0.1:8080 +export RUSTFS_WEBDAV_TLS_ENABLED=false +export RUSTFS_ACCESS_KEY= +export RUSTFS_SECRET_KEY= + +rustfs /path/to/data +``` + +Binding to `127.0.0.1` prevents other hosts from connecting to the unencrypted test endpoint. + +### HTTPS + +For a remote or production connection, enable TLS and provide a certificate directory: + +```bash +export RUSTFS_WEBDAV_ENABLE=true +export RUSTFS_WEBDAV_ADDRESS=0.0.0.0:8080 +export RUSTFS_WEBDAV_TLS_ENABLED=true +export RUSTFS_WEBDAV_CERTS_DIR=/path/to/certs + +rustfs /path/to/data +``` + +If TLS is enabled without `RUSTFS_WEBDAV_CERTS_DIR`, or the directory does not exist, WebDAV initialization fails. See [Configure TLS](/integration/tls-configured) for certificate preparation guidance. + +Allow inbound TCP traffic to the configured WebDAV port. This listener is separate from the S3 API and Console listeners. + +## Usage + +The following commands use an HTTP endpoint for local testing. Replace the endpoint and credentials with your HTTPS WebDAV endpoint and an IAM user that has the required bucket and object permissions. + +Set reusable shell variables without placing the secret directly in each command: + +```bash +export WEBDAV_URL=http://127.0.0.1:8080 +export WEBDAV_USER= +read -s WEBDAV_PASSWORD +export WEBDAV_PASSWORD +``` + +### List buckets + +Send `PROPFIND` with `Depth: 1` to list the buckets visible to the user: + +```bash +curl --user "$WEBDAV_USER:$WEBDAV_PASSWORD" \ + --request PROPFIND \ + --header "Depth: 1" \ + "$WEBDAV_URL/" +``` + +### Create a bucket + +```bash +curl --user "$WEBDAV_USER:$WEBDAV_PASSWORD" \ + --request MKCOL \ + "$WEBDAV_URL/my-bucket/" +``` + +Bucket names must follow the same naming rules as buckets created through the S3 API. + +### Upload and download an object + +Upload `/path/to/hello.txt`: + +```bash +curl --user "$WEBDAV_USER:$WEBDAV_PASSWORD" \ + --upload-file /path/to/hello.txt \ + "$WEBDAV_URL/my-bucket/hello.txt" +``` + +Download the object: + +```bash +curl --user "$WEBDAV_USER:$WEBDAV_PASSWORD" \ + --output hello.txt \ + "$WEBDAV_URL/my-bucket/hello.txt" +``` + +### Create and list a directory + +WebDAV directories below a bucket map to object key prefixes: + +```bash +curl --user "$WEBDAV_USER:$WEBDAV_PASSWORD" \ + --request MKCOL \ + "$WEBDAV_URL/my-bucket/docs/" + +curl --user "$WEBDAV_USER:$WEBDAV_PASSWORD" \ + --request PROPFIND \ + --header "Depth: 1" \ + "$WEBDAV_URL/my-bucket/docs/" +``` + +### Rename an object + +Use `MOVE` with a destination path on the same WebDAV endpoint: + +```bash +curl --user "$WEBDAV_USER:$WEBDAV_PASSWORD" \ + --request MOVE \ + --header "Destination: $WEBDAV_URL/my-bucket/greeting.txt" \ + "$WEBDAV_URL/my-bucket/hello.txt" +``` + +The IAM user needs permission to read and delete the source and to write the destination. If authorization fails, RustFS leaves the source unchanged. + +### Delete an object or bucket + +Delete an object: + +```bash +curl --user "$WEBDAV_USER:$WEBDAV_PASSWORD" \ + --request DELETE \ + "$WEBDAV_URL/my-bucket/greeting.txt" +``` + +Delete the bucket after removing its contents: + +```bash +curl --user "$WEBDAV_USER:$WEBDAV_PASSWORD" \ + --request DELETE \ + "$WEBDAV_URL/my-bucket/" +``` + +### Connect a desktop client + +Use the WebDAV endpoint in a client that supports Basic authentication: + +| Client | Connection address | +| --- | --- | +| GNOME Files | `dav://:8080/` for HTTP or `davs://:8080/` for HTTPS | +| macOS Finder | `http://:8080/` or `https://:8080/` | +| Windows File Explorer | `https://:8080/` | + +Enter the RustFS access key and secret key when the client prompts for credentials. Client behavior and supported WebDAV methods vary; use `curl` to isolate server-side errors when troubleshooting. + +## Next steps + +- [Manage credentials](/operations/credentials) +- [Configure TLS](/integration/tls-configured) +- [Check service status](/operations/status-check) \ No newline at end of file diff --git a/content/concepts/architecture.md b/content/concepts/architecture.md deleted file mode 100644 index 848a3261..00000000 --- a/content/concepts/architecture.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -title: "RustFS Architecture" -description: "Introduction to RustFS Architecture" ---- - -RustFS is a high-performance object storage system compatible with the AWS S3 API. It features a concise, lightweight, scalable, and decentralized architecture. - -Objects can be documents, videos, PDF files, or any other unstructured data. RustFS provides a scalable, flexible, and efficient solution for storing, accessing, and managing this data. Its compatibility with the AWS S3 API enables seamless integration with existing S3-based applications. - -The following diagram illustrates the architecture: - -```mermaid -flowchart LR - APP[Applications] --> S3API(["S3 API"]) - - subgraph DIST["Distributed RustFS"] - direction TB - subgraph N1["Node 1"] - direction LR - S3a[S3] - subgraph OL1["Object Layer"] - direction TB - C1[Cache] - K1[Compression] - E1[Encryption] - B1["Erasure Code · Bitrot"] - end - SL1["Storage Layer"] - J1[("JBOD / FS disks")] - S3a -->|Object API| OL1 - OL1 -->|Storage API| SL1 - SL1 <--> J1 - end - subgraph N2["Node 2"] - direction LR - S3b[S3] - subgraph OL2["Object Layer"] - direction TB - C2[Cache] - K2[Compression] - E2[Encryption] - B2["Erasure Code · Bitrot"] - end - SL2["Storage Layer"] - J2[("JBOD / FS disks")] - S3b -->|Object API| OL2 - OL2 -->|Storage API| SL2 - SL2 <--> J2 - end - NN["Node n ..."] - N1 <-->|Internal RESTful API| N2 - N2 <-->|Internal RESTful API| NN - end - - S3API --> N1 - S3API --> N2 - S3API --> NN - - classDef server fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e293b; - classDef store fill:#dcfce7,stroke:#22c55e,stroke-width:2px,color:#1e293b; - classDef svc fill:#eef2ff,stroke:#6366f1,stroke-width:2px,color:#1e293b; - classDef muted fill:#f3f4f6,stroke:#9ca3af,stroke-width:2px,color:#1e293b; - classDef accent fill:#fae8ff,stroke:#c026d3,stroke-width:2px,color:#1e293b; - class APP,NN muted - class S3API accent - class S3a,S3b,SL1,SL2 server - class C1,K1,E1,B1,C2,K2,E2,B2 svc - class J1,J2 store -``` - -This diagram represents the basic architecture of RustFS. A distributed grid uses multiple nodes to execute a single task, connected via a network to enable communication. - -## Consistency Design - -In both distributed and single-machine modes, all read and write operations strictly follow the **read-after-write** consistency model. - -## Key Concepts - -**Object**: The fundamental unit of storage in RustFS, representing files, byte streams, or any unstructured data. - -**Bucket**: A logical container for storing objects. Data is isolated between buckets. For clients, it functions similarly to a top-level directory. - -**Drive**: The physical disk that stores data, passed as a parameter when RustFS starts. All object data in RustFS is stored on these drives. - -**Set**: A group of drives. Distributed deployment automatically divides the cluster into one or more sets based on scale. Drives in each set are distributed across different locations. An object is stored within a single set. (Sometimes referred to as **Stripes**). - -Consider the following when designing the architecture and deploying devices: - -- One object is stored on one set. -- One cluster is divided into multiple sets. -- The number of drives in a set is fixed, defaulting to automatic calculation by the system based on cluster scale. -- Drives in a set should be distributed across different nodes as much as possible. - -## Architectural Design - -Traditional distributed storage architectures often rely on distinct Master nodes, Metadata nodes, and Data nodes. This complexity can make deployment challenging and introduces single points of failure—if metadata is lost, data integrity is at risk. - -RustFS adopts a decentralized, peer-to-peer architecture where all nodes are equal. This design greatly simplifies deployment and eliminates metadata bottlenecks. A single command is sufficient to start the system. - -RustFS draws inspiration from the elegant and scalable architecture of MinIO, adopting a similar design philosophy that prioritizes simplicity and reliability without compromising on features. We acknowledge MinIO's contribution to promoting the S3 protocol and setting a high standard for object storage architecture. diff --git a/content/concepts/comparison.md b/content/concepts/comparison.md deleted file mode 100644 index 9ef28a66..00000000 --- a/content/concepts/comparison.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: "RustFS vs Other Storage Products" -description: "Comparison of RustFS with mainstream object storage products" ---- - -| Parameter | Ceph | MinIO | RustFS | -| - | - | - | - | -| Development Language | C++ | Go | Rust | -| Open Source License | GPL-2.0, LGPL-2.1, LGPL-3.0 | AGPL-3.0 | Apache-2.0 | -| Metadata Center | √ | x | x | -| Block Storage | √ | x | x | -| File Storage | √ | x | x | -| Architecture | Heavy architecture design | Lightweight architecture design | Lightweight architecture design | -| Community Activity | √ | √ | √ | -| License Friendliness | Medium | Restrictive (AGPL) | Excellent (Permissive) | -| Performance | Hardware dependent | High performance, low latency | High performance, low latency | -| File Protocol | S3, RBD, CephFS, etc. | S3 | S3 | -| Ease of Use | Low | High | High | -| Scalability | EB level | EB level | EB level | -| Hardware Requirements | High | Medium | Low | -| Memory Management | Manual | GC-based | Ownership-based (No GC) | -| Scaling | High difficulty | Low difficulty | Low difficulty | -| Rebalancing | High resource consumption | Low resource consumption | Low resource consumption | -| Commercial Support | √ | √ | √ | - - - -## Global Object Storage Architectural Approaches - -Currently, distributed object storage products worldwide are mainly divided into two architectural approaches: - -1. **Centralized Metadata**: Represented by Ceph. - -2. **Decentralized Metadata**: Represented by RustFS and MinIO. - -Comparison of advantages and disadvantages: - -| Feature | Centralized Metadata | Decentralized Metadata | -| - | - | - | -| Architecture Characteristics | Dedicated metadata server or center for unified metadata management | Metadata distributed across storage nodes, no dedicated metadata server | -| Metadata Management | Efficient centralized management, fast query and update | Distributed metadata storage, avoiding single-point bottlenecks | -| Single Point of Failure | Metadata server may become a single point of failure, requiring additional high-availability design | No single node failure risk | -| Deployment Complexity | Complex deployment and maintenance, requires professional operations skills | Relatively simple deployment and maintenance, suitable for cloud-native and containerized scenarios | -| Performance Issues | Under high concurrency, metadata server may become a performance bottleneck | Small file support will consume more IOPS | -| Typical Scenarios | File systems (such as Lustre, CephFS) and scenarios requiring complex metadata | Object storage (RustFS, MinIO) and large-scale distributed systems | - - -## About Storage Speed - -RustFS and MinIO share similar design principles, with overall speed largely dependent on network bandwidth and disk I/O. Benchmarks indicate that RustFS can achieve read speeds of up to 323 GB/s and write speeds of 183 GB/s. - -RustFS and MinIO stand out as leaders in high-performance distributed object storage. Under comparable configurations, their throughput significantly exceeds that of traditional architectures like Ceph. diff --git a/content/concepts/glossary.md b/content/concepts/glossary.md deleted file mode 100644 index a0a2ef78..00000000 --- a/content/concepts/glossary.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -title: "Glossary" -description: "This article introduces commonly used vocabulary in object storage to help users quickly understand object storage" ---- - -| Term | Description | -|--------------------------|--------------------------------------------------------------------------| -| Object Storage | An architecture where data is stored as objects, replacing traditional file hierarchy structures | -| Bucket | A container for storing objects with globally unique namespace | -| Object | Basic storage unit containing data, metadata, and unique identifier (Object Key) | -| Metadata | Key-value pair information describing object attributes (such as file type, creation time) | -| S3-Compatible | Storage services compatible with Amazon S3 API standards | -| Data Durability | The probability that data remains intact and accessible over a period of time (e.g., 99.999999999%) | -| Replication | Redundancy technology that ensures data safety through multiple copies | -| Erasure Coding | A method of data protection in which data is broken into fragments, expanded and encoded with redundant data pieces | -| Cold Storage | Low-cost storage type for infrequently accessed data (such as archived data) | -| Lifecycle Management | Policies for automatically transitioning/deleting objects (e.g., move to cold storage after 30 days) | -| Versioning | Retaining historical versions of objects to prevent overwriting | -| Storage Class | Different performance/cost storage tiers (Standard, Infrequent Access, Archive) | -| Access Key | Authentication keys for API requests (Access Key ID + Secret Access Key) | -| Region | Geographic location of storage infrastructure (e.g., East China 1, US West) | -| Availability Zone (AZ) | Isolated data centers with independent power/network within the same region | -| Endpoint | Domain address for accessing storage service (e.g., us-east1.rustfs.com) | -| RESTful API | API design specification based on HTTP protocol | -| Multipart Upload | Mechanism for splitting large files for upload and merging | -| Pre-Signed URL | Temporary access links with time validity | -| Server-Side Encryption (SSE) | Automatic data encryption on server side (SSE-S3/SSE-KMS/SSE-C) | -| Client-Side Encryption (CSE) | Local encryption on client side before upload | -| Cross-Region Replication | Automatic object replication across geographic regions | -| Access Control List (ACL) | Rule list controlling access permissions for buckets/objects | -| Bucket Policy | JSON-based fine-grained permission control policies | -| IAM | Identity and Access Management system for centralized user/role permission management | -| Event Notification | Sending notifications to message queues/function computing when events trigger | -| Data Lake | Repository for centralized storage of structured/unstructured data | -| Compliance | Meeting data storage regulatory requirements like GDPR, HIPAA | -| Logging & Audit | Recording all API operation logs for auditing | -| Monitoring & Alerting | Real-time monitoring of storage usage/requests with alerting | -| CORS | Rules controlling browser cross-origin resource access | -| Transfer Acceleration | Optimizing upload/download speed through edge nodes | -| CDN Integration | Combining with Content Delivery Network for caching acceleration | -| Data Export | Process of migrating data to other storage systems | -| Data Import | Batch data migration from external systems to object storage | -| Static Website Hosting | Directly hosting HTML/CSS/JS static files through buckets | -| Hotlink Protection | Technology preventing external websites from stealing resource links | -| Request Rate Limiting | Controlling API request frequency per user/IP | -| Tagging | Adding classification tags to buckets/objects for management | -| Inventory Report | Periodically generated CSV/ORC files listing storage objects | -| Data Restoration | Restoring data from archive storage to accessible state | -| Storage Gateway | Access layer mapping object storage as a local file system; also a hybrid cloud solution connecting local systems with cloud storage | -| Data Compression | Compressing data before upload to save storage space | -| Data Deduplication | Eliminating duplicate data to reduce storage usage | -| Direct Read Archive | Technology for directly reading archived data without restoration | -| Bandwidth Control | Limiting download bandwidth to avoid network congestion | -| Concurrent Connections | Number of simultaneous data transfer connections | -| Data Migration Service | Automated migration tools (e.g., AWS Snowball) | -| Client SDK | Developer toolkits for integrating storage services (e.g., Python/Java SDK) | -| CLI | Command line management tools (e.g., aws s3 cp) | -| Web Console | Web-based management interface | -| Data Integrity Check | Verifying transmission integrity through MD5/SHA | -| Resumable Upload/Download | Continuing transfer from breakpoint after network interruption | -| Mirror Back to Source | Pulling and saving from specified origin when requested object doesn't exist | -| Canary Release | Release strategy gradually opening new features to some users | -| Soft Delete | Marking objects for deletion while maintaining recovery period | -| Object Lock | Compliance protection mechanism preventing object deletion or overwriting | -| Watermarking | Adding identification information to images/videos | -| Thumbnail Generation | Automatically creating thumbnail versions of images | -| Image Processing | Online cropping/scaling/rotation processing functions | -| Video Transcoding | Converting video formats/resolutions for different devices | -| Content Moderation | Automatically detecting inappropriate images/videos/text | -| Cost Analysis | Calculating costs by storage type/request count dimensions | -| Usage Monitoring | Real-time dashboard viewing storage/traffic/request counts | -| Storage Analytics | Tools analyzing storage patterns to optimize costs | -| Requester Pays | Billing model where data downloader bears the cost | -| Tiered Storage | Automatically moving data to lower-cost storage tiers | -| Intelligent Tiering | Automatically selecting optimal storage type based on access patterns | -| PrivateLink | Accessing object storage through internal network avoiding public exposure | -| VPC Endpoint | Entry point for securely accessing storage services within Virtual Private Cloud | -| SSL/TLS | Encrypting data transmission through HTTPS protocol | -| Client-Side Encryption | Users encrypting data themselves before upload | -| KMS | Key Management Service for centralized encryption key management | -| Permission Boundary | Limiting maximum permission scope of IAM roles/users | -| Temporary Credentials | Short-term valid access tokens (e.g., STS Token) | -| MFA Delete | Requiring multi-factor authentication to delete data | -| Immutability | Property preventing data tampering (combined with WORM model) | -| Legal Hold | Mandatory protection prohibiting data deletion/modification in compliance scenarios | -| Cross-Account Sharing | Allowing other cloud accounts to access specified storage resources | -| Prefetch Policy | Loading data into cache in advance to accelerate subsequent access | -| Cache-Control | Specifying browser/CDN caching behavior through HTTP headers | -| Delayed Deletion | Delaying deletion operations to prevent accidental actions | -| Batch Operations | Performing unified operations on multiple objects (delete/copy/restore) | -| Data Lineage | Metadata records tracking data sources and change history | -| Data Catalog | Retrieval system storing metadata information | -| Hybrid Cloud Storage | Architecture using both local storage and cloud storage | -| Edge Storage | Providing storage services at edge nodes close to data sources | -| Multi-Cloud Storage | Storage solutions across different cloud service providers | -| Storage Federation | Abstraction layer for unified management of multiple storage systems | -| Object Tag | Adding custom classification tags to objects | -| Bucket Tag | Adding management/billing related tags to buckets | -| Storage Quota | Limiting maximum capacity of buckets | -| Request Throttling | Limiting API requests per unit time | -| SLA | Service Level Agreement commitments for availability/durability (e.g., 99.9% availability) | -| Disaster Recovery | Ensuring business continuity through cross-region backups | -| Storage Topology | Distribution structure of data at physical/logical levels | -| Proximity Access | Routing user requests to nearest storage nodes | -| Global Namespace | Unified view management of cross-region buckets | -| Zero-Copy Migration | Fast data migration through metadata operations | -| SNSD (Single-Node Single-Disk) | Deployment topology with one server and one data disk, suited for testing and development; see [Single Node Single Disk Mode](../installation/linux/single-node-single-disk.md) | -| SNMD (Single-Node Multiple-Disk) | Deployment topology with one server and multiple data disks, providing disk-level redundancy via erasure coding; see [Single Node Multiple Disk Mode](../installation/linux/single-node-multiple-disk.md) | -| MNMD (Multiple-Node Multiple-Disk) | Distributed deployment topology across multiple servers and disks, recommended for production; see [Multiple Node Multiple Disk Mode](../installation/linux/multiple-node-multiple-disk.md) | diff --git a/content/concepts/introduction.md b/content/concepts/introduction.md deleted file mode 100644 index 14ecf77a..00000000 --- a/content/concepts/introduction.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: "What is RustFS?" -description: "RustFS is an open-source, distributed object storage solution released under the Apache 2.0 license." ---- - -RustFS is a distributed object storage system designed for simplicity and efficiency. It is fully compatible with the S3 protocol and released as open-source software under the Apache 2.0 license, making it friendly for commercial integration. Built with Rust, RustFS benefits from the language's memory safety and zero-cost abstractions, and is developed by a community of engineers worldwide. Its mission is to make high-performance storage, data security, and cost-efficiency accessible to everyone — including as the storage foundation for AI-era workloads. - -## Operating System and CPU Support - -RustFS runs on almost any CPU and operating system, including Linux, Unix, Windows, macOS, FreeBSD, and Docker, as well as edge gateways. -Supported CPU architectures: x86, ARM, and others. - -## RustFS Features - -- **S3 Compatible**: Fully compatible with the S3 protocol, integrating with big data ecosystems, data lakes, backup solutions, and media processing workflows. -- **Distributed Architecture**: Scales horizontally to meet storage requirements of various sizes. -- **Commercially Friendly**: Released under the Apache 2.0 license, safe and flexible for commercial integration. -- **High Performance**: Built with Rust, offering performance comparable to C/C++ with modern safety guarantees. -- **Secure by Design**: Rust's memory safety guarantees reduce common classes of security vulnerabilities. -- **Cross-Platform**: Runs natively on Windows, macOS, and Linux. -- **Extensible and Customizable**: Supports a plugin architecture, and the open-source codebase allows deep customization. -- **Cloud-Native**: Suited to containerized environments such as Docker and Kubernetes for rapid deployment. - -## Next Steps - -- [Quick Start](../installation/linux/quick-start.md) — deploy RustFS on Linux with one command. -- [Architecture](./architecture.md) — how RustFS is designed. -- [Comparison](./comparison.md) — how RustFS compares with other object storage products. diff --git a/content/concepts/limit.md b/content/concepts/limit.md deleted file mode 100644 index 735b2c0e..00000000 --- a/content/concepts/limit.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: "Usage Limits" -description: "RustFS is a simple, efficient, distributed object storage. It is fully S3 compatible, open source software released under the Apache2 license." ---- - -## S3 API Limits - -> The following standards strictly follow S3 protocol standards for specification. - -| Item | Specification | -| --------------------- | ---------------------------------- | -| Maximum object size | 5 TiB | -| Minimum object size | 0 B | -| Maximum object size for single PUT operation | Non-multipart upload: 5 GiB; Multipart upload: 5 TiB | -| Maximum number of parts per upload | 10,000 | -| Part size range | 5 MiB to 5 GiB; last part can be 0 B to 5 GiB | -| Maximum number of parts returned per list parts request | 10,000 | -| Maximum number of objects returned per list objects request | 1,000 | -| Maximum number of multipart uploads returned per list multipart uploads request | 1,000 | -| Maximum length of bucket name | 63 characters | -| Maximum length of object name | 1024 characters | -| Maximum length of each `/` separated object name segment | 255 characters | -| Maximum number of versions per single object | 10,000 | - ---- - -## Erasure Coding Limits - -> EC parameters are configured based on Reed-Solomon matrix EC algorithm. Actual limits depend on the specific Erasure Coding (EC) configuration. - -| Item | Specification | -| ---------------------------- | ------------------------------ | -| Maximum number of servers per cluster | No hard limit | -| Minimum number of servers | 1 | -| When server count is 1, minimum number of drives per server | 1 (for single-node single-drive deployment, cannot provide additional reliability or availability) | -| When server count is 2 or more, minimum number of drives per server | 1 | -| Maximum number of drives per server | No hard limit | -| Read quorum count | N − M (the number of data shards, where M is the parity shard count) | -| Write quorum count | N − M; when data and parity counts are equal, N − M + 1 | - ---- - -## Object Naming Limits - -### File System and Operating System Limits - -Object names in RustFS are primarily limited by the underlying operating system and file system. For example, Windows and some other operating systems restrict the use of certain special characters such as `^`, `*`, `|`, `\`, `/`, `&`, `"`, or `;`. - -Refer to relevant documentation for a complete list of restrictions based on your specific operating system and file system. - -RustFS recommends using Linux operating systems based on XFS file systems in production environments for better performance and compatibility. - -### Naming Conflict Handling - -In RustFS, applications must assign unique and non-conflicting keys to all objects. This includes avoiding creating objects whose names might conflict with parent object or sibling object names. RustFS will return an empty set when performing LIST operations at locations where conflicts occur. - -For example, the following operations would cause namespace conflicts: - -```bash -PUT data/hello/2025/first/a.csv -PUT data/hello/2025/first # Conflicts with existing object prefix - -PUT data/hello/2025/first/ -PUT data/hello/2025/first/vendors.csv # Conflicts with existing object -``` - -Although you can perform GET or HEAD operations on these objects, naming conflicts will cause LIST operations executed at the `hello/2025/first/` path to return empty result sets. diff --git a/content/concepts/principle/erasure-coding.md b/content/concepts/principle/erasure-coding.md deleted file mode 100644 index 25f0efcd..00000000 --- a/content/concepts/principle/erasure-coding.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -title: "Erasure Coding Principles" -description: "RustFS, as a new-generation distributed object storage system, demonstrates unique advantages in cloud storage through innovative architectural design and memory safety features. One of its core innovations is the deep application of Reed-Solomon Erasure Coding." ---- - -## Core Algorithm and Application Scope - -Reed-Solomon Code (RS Code) is an erasure code based on finite field algebraic structures. Due to its **efficient data recovery capability** and **flexible redundancy configuration**, it is widely used in multiple fields. Below, we detail its core application scenarios from two dimensions: technical fields and practical applications: - -### 1.1. Distributed Storage Systems (such as RustFS) -- **Data Sharding and Redundancy** - Divide original data into `k` shards, generate `m` parity shards (total `n=k+m`). Any loss of ≤ `m` shards can recover data. - **Example**: RS(10,4) strategy allows simultaneous loss of 4 nodes (storage utilization 71%), saving 50% storage space compared to triple replication (33%). - -- **Fault Recovery Mechanism** - Through **Gaussian elimination** or **Fast Fourier Transform (FFT)** algorithms, use surviving shards to reconstruct lost data, with recovery time inversely proportional to network bandwidth. - -- **Dynamic Adjustment Capability** - Supports runtime adjustment of `(k,m)` parameters to adapt to different storage tiers' (hot/warm/cold data) reliability requirements. - -### 1.2. Communication Transmission -- **Satellite Communication** - Handle long delay, high bit error rate issues in deep space channels (e.g., NASA Mars rover uses RS(255,223) code with error correction capability up to 16 bytes/codeword). - -- **5G NR Standards** - Use RS codes combined with CRC checks in control channels to ensure reliable transmission of critical signaling. - -- **Wireless Sensor Networks** - Solve cumulative packet loss problems in multi-hop transmission, typical configuration RS(6,2) can tolerate 33% data loss. - -### 1.3. Digital Media Storage -- **QR Codes** - Use RS codes to implement fault tolerance level adjustment (L7%, M15%, Q25%, H30%), ensuring correct decoding even with partially damaged areas. - -- **Blu-ray Discs** - Use RS(248,216) code combined with cross-interleaving to correct continuous burst errors caused by scratches. - -- **DNA Data Storage** - Add RS checksums during synthetic biomolecular chain synthesis to resist base synthesis/sequencing errors (e.g., Microsoft experimental project uses RS(4,2)). - -## Basic Concepts of Erasure Coding - -### 2.1 Evolution of Storage Redundancy -```rust -// Traditional triple replication storage -let data = "object_content"; -let replicas = vec![data.clone(), data.clone(), data.clone()]; -``` -Traditional multi-replication schemes have low storage efficiency issues (storage utilization 33%). Erasure coding technology divides data into blocks and calculates checksum information, achieving a balance between storage efficiency and reliability. - -### 2.2 Core Parameter Definitions -- **k**: Number of original data shards -- **m**: Number of parity shards -- **n**: Total number of shards (n = k + m) -- **Recovery threshold**: Any k shards can recover original data - -| Scheme Type | Redundancy | Fault Tolerance | -|------------|----------|------------| -| 3 Replicas | 200% | 2 nodes | -| RS(10,4) | 40% | 4 nodes | - -## Mathematical Principles of Reed-Solomon Codes - -### 3.1 Finite Field (Galois Field) Construction -Use GF(2^8) field (256 elements), satisfying: -$$ -α^8 + α^4 + α^3 + α^2 + 1 = 0 -$$ -Generator polynomial is `0x11D`, corresponding to binary `100011101` - -### 3.2 Encoding Matrix Construction -Vandermonde matrix example (k=2, m=2): -$$ -G = \begin{bmatrix} -1 & 0 \\ -0 & 1 \\ -1 & 1 \\ -1 & 2 -\end{bmatrix} -$$ - -### 3.3 Encoding Process -Data vector D = [d₁, d₂,..., dk] -Encoding result C = D × G - -**Generator polynomial interpolation method**: -Construct polynomial passing through k data points: -$$ -p(x) = d_1 + d_2x + ... + d_kx^{k-1} -$$ -Parity value calculation: -$$ -c_i = p(i), \quad i = k+1,...,n -$$ - -## Engineering Implementation in RustFS - -### 4.1 Data Sharding Strategy -```rust -struct Shard { - index: u8, - data: Vec, - hash: [u8; 32], -} - -fn split_data(data: &[u8], k: usize) -> Vec { - // Sharding logic implementation -} -``` -- Dynamic shard size adjustment (64 KB-4 MB) -- Streaming shard checksums using the HighwayHash256 algorithm for bitrot detection - -### 4.2 Parallel Encoding Optimization -```rust -use rayon::prelude::*; - -fn rs_encode(data: &[Shard], m: usize) -> Vec { - data.par_chunks(k).map(|chunk| { - // SIMD-accelerated matrix operations - unsafe { gf256_simd::rs_matrix_mul(chunk, &gen_matrix) } - }).collect() -} -``` -- Parallel computing framework based on Rayon -- Use AVX2 instruction set to optimize finite field operations - -### 4.3 Decoding Recovery Process -```mermaid -sequenceDiagram - Client->>Coordinator: Data read request - Coordinator->>Nodes: Query shard status - alt Sufficient available shards - Nodes->>Coordinator: Return k shards - Coordinator->>Decoder: Start decoding - Decoder->>Client: Return original data - else Insufficient shards - Coordinator->>Repairer: Trigger repair process - Repairer->>Nodes: Collect surviving shards - Repairer->>Decoder: Data reconstruction - Decoder->>Nodes: Write new shards - end -``` diff --git a/content/concepts/principle/meta.json b/content/concepts/principle/meta.json deleted file mode 100644 index 6cbea485..00000000 --- a/content/concepts/principle/meta.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "title": "Core Concepts", - "pages": [ - "[Erasure Coding](/concepts/principle/erasure-coding)" - ] -} diff --git a/content/developer/examples/aws-cli.md b/content/developer/examples/aws-cli.md index 055c67f0..2e7dad35 100644 --- a/content/developer/examples/aws-cli.md +++ b/content/developer/examples/aws-cli.md @@ -15,7 +15,7 @@ brew install awscli ## Configure -Set your [access keys](../../administration/iam/access-token.md) and region: +Set your [access keys](../../security-compliance/iam/access-token.md) and region: ```bash aws configure @@ -31,7 +31,9 @@ Default output format [None]: json Pass your RustFS address with `--endpoint-url` on every command. Replace `http://localhost:9000` with your server address. When `--endpoint-url` is set, the AWS CLI uses path-style addressing, which is what RustFS requires. :::note + If you did not set credentials at install, the local-test default is `rustfsadmin` / `rustfsadmin` — never use it beyond a throwaway local trial. + ::: ## Verify @@ -68,4 +70,4 @@ aws s3 ls s3://my-bucket --endpoint-url http://localhost:9000 ## Next steps -Build applications against RustFS with an [S3 SDK](../sdk/index.md), or manage objects with [mc](../mc.md). +Build applications against RustFS with an [S3 SDK](../sdk/index.md), or manage objects with [`rc`](/operations/rc). diff --git a/content/developer/examples/aws-sdk-go.md b/content/developer/examples/aws-sdk-go.md index b9ff138a..70958b90 100644 --- a/content/developer/examples/aws-sdk-go.md +++ b/content/developer/examples/aws-sdk-go.md @@ -15,7 +15,7 @@ go get github.com/aws/aws-sdk-go-v2/service/s3 ## Configure -Replace `http://localhost:9000` with your server address and use your own [access keys](../../administration/iam/access-token.md). RustFS requires path-style addressing (`UsePathStyle: true`): +Replace `http://localhost:9000` with your server address and use your own [access keys](../../security-compliance/iam/access-token.md). RustFS requires path-style addressing (`UsePathStyle: true`): ```go {9,11} import ( @@ -52,4 +52,4 @@ my-bucket ## Next steps -See the full [Go SDK guide](../sdk/go.md), or manage objects with [mc](../mc.md). +See the full [Go SDK guide](../sdk/go.md), or manage objects with [`rc`](/operations/rc). diff --git a/content/developer/examples/aws-sdk-js.md b/content/developer/examples/aws-sdk-js.md index 20a74216..408757a1 100644 --- a/content/developer/examples/aws-sdk-js.md +++ b/content/developer/examples/aws-sdk-js.md @@ -13,7 +13,7 @@ npm install @aws-sdk/client-s3 ## Configure -Replace `http://localhost:9000` with your server address and use your own [access keys](../../administration/iam/access-token.md). RustFS requires path-style addressing (`forcePathStyle: true`): +Replace `http://localhost:9000` with your server address and use your own [access keys](../../security-compliance/iam/access-token.md). RustFS requires path-style addressing (`forcePathStyle: true`): ```javascript title="index.mjs" {8} import { S3Client, CreateBucketCommand, PutObjectCommand, ListObjectsV2Command } from "@aws-sdk/client-s3"; @@ -43,4 +43,4 @@ hello.txt 12 ## Next steps -See the full [JavaScript SDK guide](../sdk/javascript.md), or manage objects with [mc](../mc.md). +See the full [JavaScript SDK guide](../sdk/javascript.md), or manage objects with [`rc`](/operations/rc). diff --git a/content/developer/examples/boto3.md b/content/developer/examples/boto3.md index 6b1e84e0..1f598b68 100644 --- a/content/developer/examples/boto3.md +++ b/content/developer/examples/boto3.md @@ -13,7 +13,7 @@ pip install boto3 ## Configure -Point the client at your RustFS endpoint. Replace `http://localhost:9000` with your server address, and use your own [access keys](../../administration/iam/access-token.md). RustFS requires path-style addressing, set via botocore `Config`: +Point the client at your RustFS endpoint. Replace `http://localhost:9000` with your server address, and use your own [access keys](../../security-compliance/iam/access-token.md). RustFS requires path-style addressing, set via botocore `Config`: ```python import boto3 @@ -50,4 +50,4 @@ hello.txt 12 ## Next steps -See the [S3 SDK overview](../sdk/index.md) for more languages, or manage objects with [mc](../mc.md). +See the [S3 SDK overview](../sdk/index.md) for more languages, or manage objects with [`rc`](/operations/rc). diff --git a/content/developer/examples/mc.md b/content/developer/examples/mc.md deleted file mode 100644 index 9a0d353d..00000000 --- a/content/developer/examples/mc.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: "mc (MinIO Client)" -description: "Connect the MinIO Client (mc) to RustFS and perform basic object operations." ---- - -[mc](https://min.io/docs/minio/linux/reference/minio-mc.html) is the MinIO command-line client for S3-compatible object storage, and it works with RustFS out of the box. - -## Install - -```bash -brew install minio/stable/mc -``` - -Or download a binary from the [official install guide](https://min.io/docs/minio/linux/reference/minio-mc.html#install-mc). - -## Configure - -Create an alias pointing at your RustFS endpoint. Replace `http://localhost:9000` with your server address, and use your own [access keys](../../administration/iam/access-token.md): - -```bash -mc alias set rustfs http://localhost:9000 -``` - -```text -Added `rustfs` successfully. -``` - -`mc` uses path-style requests by default, which is what RustFS expects. The default region is `us-east-1`. - -## Verify - -Create a bucket: - -```bash -mc mb rustfs/my-bucket -``` - -```text -Bucket created successfully `rustfs/my-bucket`. -``` - -Upload a file: - -```bash -mc cp /path/to/hello.txt rustfs/my-bucket/ -``` - -```text -/path/to/hello.txt: 12 B / 12 B 100.00% 1.2 KiB/s 0s -``` - -List the bucket: - -```bash -mc ls rustfs/my-bucket -``` - -```text -[2026-07-15 10:30:00 UTC] 12B STANDARD hello.txt -``` - -## Next steps - -See the [full mc guide](../mc.md) for bucket policies, mirroring, and more operations. diff --git a/content/developer/examples/meta.json b/content/developer/examples/meta.json index 062e98db..4742275a 100644 --- a/content/developer/examples/meta.json +++ b/content/developer/examples/meta.json @@ -1,7 +1,7 @@ { "title": "Examples", "pages": [ - "[mc (MinIO Client)](/developer/examples/mc)", + "[rc](/operations/rc)", "[AWS CLI](/developer/examples/aws-cli)", "[s3cmd](/developer/examples/s3cmd)", "[rclone](/developer/examples/rclone)", diff --git a/content/developer/examples/rclone.md b/content/developer/examples/rclone.md index 299e56dd..b93bedbf 100644 --- a/content/developer/examples/rclone.md +++ b/content/developer/examples/rclone.md @@ -15,7 +15,7 @@ Or see the [official install guide](https://rclone.org/install/). ## Configure -Add a remote to `~/.config/rclone/rclone.conf`. Replace `http://localhost:9000` with your server address, and use your own [access keys](../../administration/iam/access-token.md). `force_path_style = true` is required because RustFS uses path-style addressing: +Add a remote to `~/.config/rclone/rclone.conf`. Replace `http://localhost:9000` with your server address, and use your own [access keys](../../security-compliance/iam/access-token.md). `force_path_style = true` is required because RustFS uses path-style addressing: ```ini title="~/.config/rclone/rclone.conf" [rustfs] @@ -56,4 +56,4 @@ rclone ls rustfs:my-bucket ## Next steps -Build applications against RustFS with an [S3 SDK](../sdk/index.md), or manage objects with [mc](../mc.md). +Build applications against RustFS with an [S3 SDK](../sdk/index.md), or manage objects with [`rc`](/operations/rc). diff --git a/content/developer/examples/s3cmd.md b/content/developer/examples/s3cmd.md index 3e6e5e72..16fbf31c 100644 --- a/content/developer/examples/s3cmd.md +++ b/content/developer/examples/s3cmd.md @@ -18,7 +18,7 @@ pip install s3cmd ## Configure -Create `~/.s3cfg`. Replace `localhost:9000` with your server address and use your own [access keys](../../administration/iam/access-token.md). RustFS uses path-style addressing, so set the bucket host to the same endpoint: +Create `~/.s3cfg`. Replace `localhost:9000` with your server address and use your own [access keys](../../security-compliance/iam/access-token.md). RustFS uses path-style addressing, so set the bucket host to the same endpoint: ```ini title="~/.s3cfg" [default] @@ -53,4 +53,4 @@ upload: '/path/to/hello.txt' -> 's3://my-bucket/hello.txt' [1 of 1] ## Next steps -See the [SDK overview](../sdk/index.md) to connect an application, or manage objects with [mc](../mc.md). +See the [SDK overview](../sdk/index.md) to connect an application, or manage objects with [`rc`](/operations/rc). diff --git a/content/developer/examples/terraform.md b/content/developer/examples/terraform.md index c2557d6c..08b7ea2d 100644 --- a/content/developer/examples/terraform.md +++ b/content/developer/examples/terraform.md @@ -7,7 +7,7 @@ The [Terraform AWS provider](https://registry.terraform.io/providers/hashicorp/a ## Configure -Replace `http://localhost:9000` with your server address and use your own [access keys](../../administration/iam/access-token.md). The skip flags stop the provider from calling AWS-only metadata and STS endpoints: +Replace `http://localhost:9000` with your server address and use your own [access keys](../../security-compliance/iam/access-token.md). The skip flags stop the provider from calling AWS-only metadata and STS endpoints: ```hcl title="main.tf" provider "aws" { diff --git a/content/developer/index.md b/content/developer/index.md index 3e4eeafb..5141f84f 100644 --- a/content/developer/index.md +++ b/content/developer/index.md @@ -1,12 +1,12 @@ --- -title: RustFS Developer Guide -description: Guide to using STS, MCP, MinIO Client, SDKs, and APIs with RustFS. +title: Developer +description: "Integrate applications with RustFS by using STS, MCP, SDKs, and the S3-compatible API." --- -This guide provides instructions for integrating and interacting with RustFS using STS, MCP (Model Context Protocol), MinIO Client (`mc`), SDKs, and APIs. It is designed to enable developers to build efficient and scalable applications on top of RustFS. This document covers the following topics: +Use this section to integrate applications with RustFS through temporary credentials, the Model Context Protocol (MCP), SDKs, and the S3-compatible API. -- STS -- [MCP Usage Guide](./mcp.md) -- [MinIO Client Usage Guide](./mc.md) -- [SDK Usage Guide](./sdk/index.md) -- API Usage Guide \ No newline at end of file +- [Security Token Service (STS)](/security-compliance/iam/sts) explains how to request temporary credentials. +- [MCP](/developer/mcp) connects AI tools and agents to RustFS. +- [SDKs](/developer/sdk) provide language-specific integration examples. +- [Integration guides](/developer/integration) cover proxies and data platforms. +- [S3 API](/administration/protocols/s3) documents client configuration and compatibility considerations. diff --git a/content/developer/integration/big-data/iceberg.md b/content/developer/integration/big-data/iceberg.md new file mode 100644 index 00000000..568a3699 --- /dev/null +++ b/content/developer/integration/big-data/iceberg.md @@ -0,0 +1,271 @@ +--- +title: "Apache Iceberg" +description: "Run Apache Iceberg with Spark, a REST catalog, and RustFS object storage using Docker Compose." +--- + +This guide runs **Apache Iceberg** with Spark, an Iceberg REST catalog, and **RustFS** as the S3-compatible warehouse. You will create an Iceberg table, write rows, query them, and verify that the table files are stored in RustFS. + +You need Docker with the Compose plugin and enough local resources to run four containers. This deployment is intended for local integration testing, not production. + +:::note[Upstream status] + +Apache Iceberg [PR #14928](https://github.com/apache/iceberg/pull/14928) demonstrated the same Spark, REST catalog, `S3FileIO`, and RustFS workflow, including table creation and writes. The pull request was closed without merging, and the current [Spark quickstart](https://iceberg.apache.org/spark-quickstart/#docker-compose) still uses another S3-compatible store. The configuration below therefore documents a RustFS integration rather than an Apache Iceberg default. + +::: + +## Architecture + +```mermaid +flowchart LR + Spark["Spark SQL"] -->|REST catalog API| Catalog["Iceberg REST catalog"] + Spark -->|S3FileIO| RustFS["RustFS S3 API"] + Catalog -->|S3FileIO| RustFS + Init["Bucket initializer"] -->|Create my-bucket| RustFS +``` + +Spark uses the REST service for catalog operations. Both Spark and the REST catalog receive the RustFS endpoint, region, credentials, and path-style setting so they can access metadata and data files in `s3://my-bucket/warehouse`. + +## 1. Create the project files + +Create a working directory: + +```bash +mkdir rustfs-iceberg +cd rustfs-iceberg +``` + +Create an environment file and replace both credential placeholders: + +```ini title=".env" +RUSTFS_ACCESS_KEY= +RUSTFS_SECRET_KEY= +``` + +Use dedicated credentials for the warehouse bucket. Do not commit `.env` to source control. + +Create the Spark catalog configuration: + +```ini title="spark-defaults.conf" +spark.sql.extensions org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions +spark.sql.catalog.demo org.apache.iceberg.spark.SparkCatalog +spark.sql.catalog.demo.type rest +spark.sql.catalog.demo.uri http://rest:8181 +spark.sql.catalog.demo.io-impl org.apache.iceberg.aws.s3.S3FileIO +spark.sql.catalog.demo.warehouse s3://my-bucket/warehouse +spark.sql.catalog.demo.s3.endpoint http://rustfs:9000 +spark.sql.catalog.demo.s3.path-style-access true +spark.sql.defaultCatalog demo +spark.sql.catalogImplementation in-memory +``` + +Path-style access is required for this container-network endpoint. The hostname `rustfs` is resolvable only inside the Compose network; clients running on the host use `http://localhost:9000` instead. + +Create the Compose file: + +```yaml title="compose.yaml" +services: + rustfs: + image: rustfs/rustfs:1.0.0-alpha.83 + environment: + RUSTFS_ACCESS_KEY: ${RUSTFS_ACCESS_KEY} + RUSTFS_SECRET_KEY: ${RUSTFS_SECRET_KEY} + RUSTFS_VOLUMES: /data + RUSTFS_ADDRESS: 0.0.0.0:9000 + RUSTFS_CONSOLE_ADDRESS: 0.0.0.0:9001 + RUSTFS_CONSOLE_ENABLE: "true" + volumes: + - rustfs-data:/data + ports: + - "9000:9000" + - "9001:9001" + networks: + - iceberg + + create-bucket: + image: rustfs/rc:latest + depends_on: + - rustfs + environment: + RUSTFS_ACCESS_KEY: ${RUSTFS_ACCESS_KEY} + RUSTFS_SECRET_KEY: ${RUSTFS_SECRET_KEY} + entrypoint: + - /bin/sh + - -c + - | + until /usr/bin/rc alias set rustfs http://rustfs:9000 "$${RUSTFS_ACCESS_KEY}" "$${RUSTFS_SECRET_KEY}"; do + echo "Waiting for RustFS..." + sleep 2 + done + /usr/bin/rc ls rustfs/my-bucket >/dev/null 2>&1 || /usr/bin/rc mb rustfs/my-bucket + networks: + - iceberg + + rest: + image: apache/iceberg-rest-fixture + depends_on: + create-bucket: + condition: service_completed_successfully + environment: + AWS_ACCESS_KEY_ID: ${RUSTFS_ACCESS_KEY} + AWS_SECRET_ACCESS_KEY: ${RUSTFS_SECRET_KEY} + AWS_REGION: us-east-1 + CATALOG_WAREHOUSE: s3://my-bucket/warehouse + CATALOG_IO__IMPL: org.apache.iceberg.aws.s3.S3FileIO + CATALOG_S3_ENDPOINT: http://rustfs:9000 + CATALOG_S3_PATH__STYLE__ACCESS: "true" + ports: + - "8181:8181" + networks: + - iceberg + + spark-iceberg: + image: tabulario/spark-iceberg + depends_on: + create-bucket: + condition: service_completed_successfully + rest: + condition: service_started + environment: + AWS_ACCESS_KEY_ID: ${RUSTFS_ACCESS_KEY} + AWS_SECRET_ACCESS_KEY: ${RUSTFS_SECRET_KEY} + AWS_REGION: us-east-1 + volumes: + - ./spark-defaults.conf:/opt/spark/conf/spark-defaults.conf:ro + ports: + - "8888:8888" + - "8080:8080" + networks: + - iceberg + +networks: + iceberg: + +volumes: + rustfs-data: +``` + +The [`rc` image](https://github.com/rustfs/cli) provides the official RustFS command-line client. The initializer checks for `my-bucket` before creating it, so repeated starts do not delete existing warehouse data. The RustFS volume preserves warehouse objects across container recreation. + +:::warning[Image versions] + +The Apache Iceberg quickstart images are published without stable version tags in the upstream example. Before using this pattern beyond local testing, pin every image to a tested tag or digest and validate the Spark, Iceberg runtime, and REST catalog versions together. + +::: + +## 2. Validate and start the deployment + +Resolve the Compose file before starting containers: + +```bash +docker compose config +``` + +Start the services and wait for the bucket initializer to finish: + +```bash +docker compose up -d +docker compose ps -a +``` + +The `create-bucket` service should show an exit code of `0`. Check its logs if it does not complete: + +```bash +docker compose logs create-bucket +``` + +Open the RustFS Console at `http://localhost:9001`. The REST catalog is available at `http://localhost:8181`, and the Spark notebook server is available at `http://localhost:8888`. + +## 3. Create and query an Iceberg table + +Start Spark SQL: + +```bash +docker compose exec spark-iceberg spark-sql +``` + +Create a namespace and a partitioned table: + +```sql +CREATE NAMESPACE IF NOT EXISTS demo.nyc; + +CREATE TABLE demo.nyc.taxis +( + vendor_id bigint, + trip_id bigint, + trip_distance float, + fare_amount double, + store_and_fwd_flag string +) +PARTITIONED BY (vendor_id); +``` + +Insert and query sample rows: + +```sql +INSERT INTO demo.nyc.taxis +VALUES + (1, 1000371, 1.8, 15.32, 'N'), + (2, 1000372, 2.5, 22.15, 'N'), + (2, 1000373, 0.9, 9.01, 'N'), + (1, 1000374, 8.4, 42.13, 'Y'); + +SELECT * FROM demo.nyc.taxis ORDER BY trip_id; +``` + +The query should return four rows: + +```text +1 1000371 1.8 15.32 N +2 1000372 2.5 22.15 N +2 1000373 0.9 9.01 N +1 1000374 8.4 42.13 Y +``` + +## 4. Verify objects in RustFS + +List the warehouse from the bucket-initializer image: + +```bash +docker compose run --rm --entrypoint /bin/sh create-bucket -c \ + '/usr/bin/rc alias set rustfs http://rustfs:9000 "$RUSTFS_ACCESS_KEY" "$RUSTFS_SECRET_KEY" >/dev/null && /usr/bin/rc find rustfs/my-bucket/warehouse' +``` + +The output should include Iceberg metadata and data objects below the `warehouse/nyc/taxis` prefix. You can also inspect the `my-bucket` bucket in the RustFS Console. + +## 5. Stop or reset the stack + +Stop the containers while keeping the RustFS data volume: + +```bash +docker compose down +``` + +To delete the local warehouse and start from an empty RustFS volume, explicitly include `--volumes`: + +```bash +docker compose down --volumes +``` + +## Troubleshooting + +### Spark cannot reach RustFS + +Use `http://rustfs:9000` inside Compose. `http://localhost:9000` refers to the Spark container itself when used in `spark-defaults.conf`. + +Confirm that `spark.sql.catalog.demo.s3.path-style-access` is `true`. Virtual-hosted requests require additional RustFS domain and DNS configuration. + +### The catalog returns an S3 error + +Check that the credentials in `.env` match the RustFS credentials and that the `create-bucket` service completed successfully: + +```bash +docker compose logs create-bucket rest +``` + +The REST catalog property uses doubled underscores in `CATALOG_IO__IMPL` and `CATALOG_S3_PATH__STYLE__ACCESS`; the fixture converts them to the dotted and hyphenated Iceberg property names. + +## Next steps + +- Review [S3 compatibility notes](/administration/protocols/s3) before adopting additional Iceberg operations. +- Create dedicated production credentials with [Access Key Management](/security-compliance/iam/access-token). +- Follow the [Apache Iceberg Spark documentation](https://iceberg.apache.org/docs/latest/spark-getting-started/) to configure your existing Spark environment. \ No newline at end of file diff --git a/content/developer/integration/big-data/index.md b/content/developer/integration/big-data/index.md new file mode 100644 index 00000000..89f9fe9f --- /dev/null +++ b/content/developer/integration/big-data/index.md @@ -0,0 +1,13 @@ +--- +title: "Big Data" +description: "Connect big data systems to RustFS through S3-compatible object storage interfaces." +--- + +Use **RustFS** as the object storage layer for big data systems that support an S3-compatible endpoint. + +## Systems + +- [Iceberg](./iceberg.md) +- [Milvus](./milvus.md) + +Keep application data in a dedicated bucket and prefix, and use credentials scoped to the required bucket operations. \ No newline at end of file diff --git a/content/developer/integration/big-data/meta.json b/content/developer/integration/big-data/meta.json new file mode 100644 index 00000000..c3f673eb --- /dev/null +++ b/content/developer/integration/big-data/meta.json @@ -0,0 +1,7 @@ +{ + "title": "Big Data", + "pages": [ + "iceberg", + "milvus" + ] +} \ No newline at end of file diff --git a/content/developer/integration/big-data/milvus.md b/content/developer/integration/big-data/milvus.md new file mode 100644 index 00000000..869be2d3 --- /dev/null +++ b/content/developer/integration/big-data/milvus.md @@ -0,0 +1,352 @@ +--- +title: "Milvus" +description: "Run Milvus with RustFS as its S3-compatible object storage backend using Docker Compose." +--- + +This guide runs **Milvus Standalone** with **RustFS** as its S3-compatible object storage backend. You will start Milvus, etcd, RustFS, and Attu; insert sample vectors; and verify that Milvus persists objects in RustFS. + +You need Docker with the Compose plugin and Python 3.9 or later. This deployment is intended for local integration testing, not production. + +:::note[Milvus configuration names] + +Milvus groups S3-compatible storage settings under the `minio` configuration key. The name does not require a MinIO server. In this guide, `minio.address` points to the RustFS service, and Milvus uses the S3 API exposed by RustFS. + +::: + +## Architecture + +```mermaid +flowchart LR + Client["Milvus client"] -->|gRPC :19530| Milvus["Milvus Standalone"] + Attu["Attu"] -->|gRPC :19530| Milvus + Milvus -->|Metadata| Etcd["etcd"] + Milvus -->|S3 API| RustFS["RustFS"] + Init["Bucket initializer"] -->|Create my-bucket| RustFS +``` + +Milvus stores service metadata in etcd and persists vector data, indexes, and related objects under `s3://my-bucket/milvus` in RustFS. The local Milvus volume remains necessary for runtime data and caches. + +## 1. Create the project files + +Create a working directory: + +```bash +mkdir rustfs-milvus +cd rustfs-milvus +``` + +Create an environment file and replace both credential placeholders: + +```ini title=".env" +RUSTFS_ACCESS_KEY= +RUSTFS_SECRET_KEY= +``` + +Use dedicated credentials for the Milvus bucket. Do not commit `.env` to source control. + +Create the Milvus storage override: + +```yaml title="user.yaml" +common: + storageType: remote + +minio: + address: rustfs:9000 + port: 9000 + bucketName: my-bucket + rootPath: milvus + useSSL: false + useIAM: false + cloudProvider: aws + region: us-east-1 + useVirtualHost: false +``` + +`useVirtualHost: false` selects path-style S3 requests. The hostname `rustfs` resolves inside the Compose network; clients on the host use `http://localhost:9000`. + +Create the Compose file: + +```yaml title="compose.yaml" +services: + etcd: + image: quay.io/coreos/etcd:v3.5.18 + environment: + ETCD_AUTO_COMPACTION_MODE: revision + ETCD_AUTO_COMPACTION_RETENTION: "1000" + ETCD_QUOTA_BACKEND_BYTES: "4294967296" + ETCD_SNAPSHOT_COUNT: "50000" + command: + - etcd + - --advertise-client-urls=http://etcd:2379 + - --listen-client-urls=http://0.0.0.0:2379 + - --data-dir=/etcd + volumes: + - etcd-data:/etcd + healthcheck: + test: ["CMD", "etcdctl", "endpoint", "health"] + interval: 30s + timeout: 20s + retries: 3 + networks: + - milvus + + rustfs: + image: rustfs/rustfs:1.0.0-alpha.83 + environment: + RUSTFS_ACCESS_KEY: ${RUSTFS_ACCESS_KEY} + RUSTFS_SECRET_KEY: ${RUSTFS_SECRET_KEY} + RUSTFS_VOLUMES: /data + RUSTFS_ADDRESS: 0.0.0.0:9000 + RUSTFS_CONSOLE_ADDRESS: 0.0.0.0:9001 + RUSTFS_CONSOLE_ENABLE: "true" + volumes: + - rustfs-data:/data + ports: + - "9000:9000" + - "9001:9001" + healthcheck: + test: ["CMD-SHELL", "curl -fsS http://localhost:9000/health/ready"] + interval: 10s + timeout: 5s + retries: 12 + start_period: 20s + networks: + - milvus + + create-bucket: + image: rustfs/rc:latest + depends_on: + rustfs: + condition: service_healthy + environment: + RUSTFS_ACCESS_KEY: ${RUSTFS_ACCESS_KEY} + RUSTFS_SECRET_KEY: ${RUSTFS_SECRET_KEY} + entrypoint: + - /bin/sh + - -c + - | + /usr/bin/rc alias set rustfs http://rustfs:9000 "$${RUSTFS_ACCESS_KEY}" "$${RUSTFS_SECRET_KEY}" \ + --region us-east-1 --bucket-lookup path + /usr/bin/rc bucket create rustfs/my-bucket --ignore-existing + networks: + - milvus + + standalone: + image: milvusdb/milvus:v2.6.0 + command: ["milvus", "run", "standalone"] + security_opt: + - seccomp:unconfined + depends_on: + etcd: + condition: service_healthy + create-bucket: + condition: service_completed_successfully + environment: + ETCD_ENDPOINTS: etcd:2379 + MINIO_ADDRESS: rustfs:9000 + MINIO_ACCESS_KEY_ID: ${RUSTFS_ACCESS_KEY} + MINIO_SECRET_ACCESS_KEY: ${RUSTFS_SECRET_KEY} + MINIO_REGION: us-east-1 + MQ_TYPE: woodpecker + volumes: + - milvus-data:/var/lib/milvus + - ./user.yaml:/milvus/configs/user.yaml:ro + ports: + - "19530:19530" + - "9091:9091" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9091/healthz"] + interval: 30s + timeout: 20s + retries: 3 + start_period: 90s + networks: + - milvus + + attu: + image: zilliz/attu:v2.6.5 + depends_on: + standalone: + condition: service_healthy + environment: + MILVUS_URL: standalone:19530 + ports: + - "8000:3000" + networks: + - milvus + +networks: + milvus: + +volumes: + etcd-data: + rustfs-data: + milvus-data: +``` + +The `create-bucket` service uses the official [`rc`](https://github.com/rustfs/cli) image and exits after ensuring that `my-bucket` exists. Named volumes preserve etcd metadata, RustFS objects, and Milvus local data when containers are recreated. + +:::warning[Protect local service ports] + +The Compose file publishes the RustFS API and Console, Milvus gRPC and health ports, and Attu on the host for local testing. Do not expose these ports to an untrusted network. Production deployments require scoped credentials, TLS, authentication, resource planning, backups, and independently operated dependencies. + +::: + +## 2. Validate and start the deployment + +Resolve the Compose file before starting containers: + +```bash +docker compose config +``` + +Start the services: + +```bash +docker compose up -d +docker compose ps -a +``` + +The `create-bucket` service should exit with code `0`, and `etcd`, `rustfs`, and `standalone` should become healthy. Inspect logs if a service does not reach its expected state: + +```bash +docker compose logs create-bucket rustfs standalone +``` + +Open these local interfaces: + +- RustFS Console: `http://localhost:9001` +- Attu: `http://localhost:8000` +- Milvus health endpoint: `http://localhost:9091/healthz` + +Attu connects to `standalone:19530` through the Compose network. If Attu asks for a connection address, use that service name instead of `localhost:19530`. + +## 3. Insert and query sample vectors + +Create a Python virtual environment and install the Milvus client version that matches the server release: + +```bash +python3 -m venv .venv +source .venv/bin/activate +python -m pip install "pymilvus==2.6.0" +``` + +Create a test script: + +```python title="verify_milvus.py" +from pymilvus import MilvusClient + +client = MilvusClient(uri="http://localhost:19530") +collection_name = "rustfs_demo" + +if client.has_collection(collection_name=collection_name): + client.drop_collection(collection_name=collection_name) + +client.create_collection( + collection_name=collection_name, + dimension=4, +) + +client.insert( + collection_name=collection_name, + data=[ + {"id": 1, "vector": [0.1, 0.2, 0.3, 0.4]}, + {"id": 2, "vector": [0.2, 0.3, 0.4, 0.5]}, + {"id": 3, "vector": [0.9, 0.8, 0.7, 0.6]}, + ], +) + +client.flush(collection_name=collection_name) +results = client.search( + collection_name=collection_name, + data=[[0.1, 0.2, 0.3, 0.4]], + limit=2, + output_fields=["id"], +) + +print(results) +client.close() +``` + +Run the script: + +```bash +python verify_milvus.py +``` + +The result should rank the row with ID `1` first. Open Attu and confirm that the `rustfs_demo` collection contains three entities. + +## 4. Verify Milvus objects in RustFS + +Use the bucket-initializer image to list objects below the configured `milvus` root path: + +```bash +docker compose run --rm --entrypoint /bin/sh create-bucket -c \ + '/usr/bin/rc alias set rustfs http://rustfs:9000 "$RUSTFS_ACCESS_KEY" "$RUSTFS_SECRET_KEY" --region us-east-1 --bucket-lookup path >/dev/null && /usr/bin/rc find rustfs/my-bucket/milvus' +``` + +The output should contain objects created by Milvus below the `milvus/` prefix. You can also open `my-bucket` in the RustFS Console. + +Milvus may buffer or compact data before every expected object appears. The successful insert, flush, query, and RustFS object listing together validate the integration path. + +## 5. Stop or reset the stack + +Stop the containers while retaining all named volumes: + +```bash +docker compose down +``` + +To delete the local test data, including the Milvus bucket contents and etcd metadata, explicitly remove the volumes: + +```bash +docker compose down --volumes +``` + +:::warning[Reset removes the test data] + +The `--volumes` option permanently deletes the named volumes used by this Compose project. Do not run it against data you need to retain. + +::: + +## Troubleshooting + +### Milvus cannot reach RustFS + +Use `rustfs:9000` as the S3 endpoint inside Compose. `localhost:9000` inside the Milvus container refers to that container, not RustFS. + +Confirm that `useVirtualHost` remains `false` and that the credential values passed to Milvus match the RustFS credentials: + +```bash +docker compose logs standalone rustfs +``` + +### The bucket initializer fails + +Check RustFS readiness and the initializer logs: + +```bash +curl -fsS http://localhost:9000/health/ready +docker compose logs create-bucket +``` + +Verify that `.env` contains non-empty credentials and that `docker compose config` resolves both variables. + +### Milvus starts without existing data + +Do not change `minio.bucketName`, `minio.rootPath`, or the etcd root for an existing deployment. Confirm that the `rustfs-data`, `etcd-data`, and `milvus-data` volumes still exist and that the same Compose project name is in use. + +### Attu cannot connect + +The Attu container must use `standalone:19530`. A browser or host-side client uses `localhost:19530`. Check Milvus health and Attu logs: + +```bash +curl -fsS http://localhost:9091/healthz +docker compose logs attu standalone +``` + +## Next steps + +- Review [S3 compatibility notes](/administration/protocols/s3) before enabling additional Milvus storage features. +- Create dedicated production credentials with [Access Key Management](/security-compliance/iam/access-token). +- Follow the [Milvus documentation](https://milvus.io/docs) when adapting this local pattern to a managed or distributed deployment. \ No newline at end of file diff --git a/content/developer/integration/index.md b/content/developer/integration/index.md new file mode 100644 index 00000000..36086197 --- /dev/null +++ b/content/developer/integration/index.md @@ -0,0 +1,13 @@ +--- +title: "Integration" +description: "Integrate RustFS with reverse proxies and big data systems." +--- + +Use this section to connect **RustFS** to infrastructure and application platforms through its S3-compatible API. + +## Integration categories + +- [Reverse Proxy](./reverse-proxy/index.md) covers Nginx, Traefik, Caddy, and HAProxy. +- [Big Data](./big-data/index.md) covers Iceberg. + +Each guide identifies the RustFS endpoint and addressing requirements to use when configuring the integrating system. \ No newline at end of file diff --git a/content/developer/integration/meta.json b/content/developer/integration/meta.json new file mode 100644 index 00000000..96a0425f --- /dev/null +++ b/content/developer/integration/meta.json @@ -0,0 +1,7 @@ +{ + "title": "Integration", + "pages": [ + "reverse-proxy", + "big-data" + ] +} \ No newline at end of file diff --git a/content/developer/integration/reverse-proxy/caddy.md b/content/developer/integration/reverse-proxy/caddy.md new file mode 100644 index 00000000..57c1a55b --- /dev/null +++ b/content/developer/integration/reverse-proxy/caddy.md @@ -0,0 +1,207 @@ +--- +title: "Caddy" +description: "Deploy RustFS behind Caddy with automatic HTTPS for separate S3 API and Console hostnames." +--- + +Use **Caddy** to obtain and renew TLS certificates automatically and route separate hostnames to the RustFS S3 API and Console. This deployment runs Caddy and a single-node RustFS instance with Docker Compose. You need Docker Engine, Docker Compose, a public server, and two DNS records that resolve to that server. + +This guide uses these example hostnames: + +- `s3.example.com` for the S3 API +- `console.example.com` for the Console + +Replace them with your public hostnames. Caddy's default ACME challenges require inbound access to ports `80` and `443`. + +:::warning[Serve S3 from the root path] + +Do not publish the S3 API under a path such as `/s3/`. AWS Signature Version 4 includes the request path and host, so rewriting either value can invalidate signed requests. + +::: + +## 1. Create the deployment directory + +Create a directory for the deployment: + +```bash +mkdir rustfs-caddy +cd rustfs-caddy +``` + +## 2. Set deployment variables + +Create an environment file and replace each value: + +```ini title=".env" +S3_HOSTNAME=s3.example.com +CONSOLE_HOSTNAME=console.example.com +ACME_EMAIL=admin@example.com +RUSTFS_ACCESS_KEY= +RUSTFS_SECRET_KEY= +``` + +Use an email address that receives certificate notices. Do not commit `.env` to source control. + +## 3. Configure Caddy + +Create a Caddyfile with one site block for each RustFS endpoint: + +```text title="Caddyfile" +{ + email {$ACME_EMAIL} +} + +{$S3_HOSTNAME} { + reverse_proxy rustfs:9000 { + health_uri /health/ready + health_interval 10s + health_timeout 5s + health_fails 3 + health_passes 2 + lb_try_duration 5s + } +} + +{$CONSOLE_HOSTNAME} { + reverse_proxy rustfs:9001 { + health_uri /rustfs/console/health + health_interval 10s + health_timeout 5s + health_fails 3 + health_passes 2 + lb_try_duration 5s + } +} +``` + +Caddy preserves the incoming `Host` header, HTTP method, and request URI by default. It also forwards client information through `X-Forwarded-*` headers and handles Console WebSocket upgrades without additional header rules. + +## 4. Create the Compose file + +Create the Caddy and RustFS services: + +```yaml title="compose.yaml" +services: + caddy: + image: caddy:2.10-alpine + restart: unless-stopped + depends_on: + rustfs: + condition: service_healthy + environment: + S3_HOSTNAME: ${S3_HOSTNAME} + CONSOLE_HOSTNAME: ${CONSOLE_HOSTNAME} + ACME_EMAIL: ${ACME_EMAIL} + ports: + - "80:80" + - "443:443" + - "443:443/udp" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + - caddy-data:/data + - caddy-config:/config + networks: + - rustfs + + rustfs: + image: rustfs/rustfs:latest + restart: unless-stopped + environment: + RUSTFS_ACCESS_KEY: ${RUSTFS_ACCESS_KEY} + RUSTFS_SECRET_KEY: ${RUSTFS_SECRET_KEY} + RUSTFS_CONSOLE_ENABLE: "true" + RUSTFS_ADDRESS: "0.0.0.0:9000" + RUSTFS_CONSOLE_ADDRESS: "0.0.0.0:9001" + expose: + - "9000" + - "9001" + volumes: + - rustfs-data:/data + healthcheck: + test: ["CMD-SHELL", "curl --fail http://127.0.0.1:9000/health/ready && curl --fail http://127.0.0.1:9001/rustfs/console/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + networks: + - rustfs + +volumes: + caddy-data: + caddy-config: + rustfs-data: + +networks: + rustfs: +``` + +The persistent `caddy-data` volume stores certificates, private keys, and ACME account state. Back up this volume and do not share its contents. Only Caddy publishes host ports; RustFS remains reachable inside the Compose network. + +## 5. Validate and start the deployment + +Render the Compose configuration and start RustFS: + +```bash +docker compose config +docker compose up -d rustfs +``` + +Validate the Caddyfile with the same image used by the deployment: + +```bash +docker compose run --rm --no-deps caddy caddy validate --config /etc/caddy/Caddyfile +``` + +Start Caddy and check both services: + +```bash +docker compose up -d caddy +docker compose ps +docker compose logs --follow caddy +``` + +Caddy obtains certificates in the background and redirects HTTP requests to HTTPS. If issuance fails, confirm that both DNS records resolve to this host, ports `80` and `443` are reachable, and the `caddy-data` volume is writable. + +## 6. Verify both endpoints + +Verify the API and Console through their public HTTPS hostnames: + +```bash +curl --fail https://s3.example.com/health/ready +curl --fail https://console.example.com/rustfs/console/health +``` + +Configure S3 clients with `https://s3.example.com` as the endpoint and enable path-style addressing. Open `https://console.example.com` to sign in to the Console. + +## Multi-node upstreams + +For a distributed RustFS deployment, list every node in the corresponding site block: + +```text title="Caddyfile" +{$S3_HOSTNAME} { + reverse_proxy node1.example.net:9000 node2.example.net:9000 node3.example.net:9000 node4.example.net:9000 { + lb_policy least_conn + health_uri /health/ready + health_interval 10s + health_timeout 5s + lb_try_duration 5s + } +} + +{$CONSOLE_HOSTNAME} { + reverse_proxy node1.example.net:9001 node2.example.net:9001 node3.example.net:9001 node4.example.net:9001 { + lb_policy cookie rustfs_console + health_uri /rustfs/console/health + health_interval 10s + health_timeout 5s + lb_try_duration 5s + } +} +``` + +Replace `` with a random secret shared by all Caddy instances. Console affinity keeps an in-progress OpenID Connect login on the RustFS node that created its `state`. Keep port `9000` open directly between RustFS nodes because internal node RPC uses the same listener. + +## Next steps + +- [Configure an S3 client](/developer/examples/aws-cli) +- [Enable virtual-hosted-style bucket URLs](/integration/virtual) +- [Review health and readiness endpoints](/operations/status-check) \ No newline at end of file diff --git a/content/developer/integration/reverse-proxy/haproxy.md b/content/developer/integration/reverse-proxy/haproxy.md new file mode 100644 index 00000000..faac09ef --- /dev/null +++ b/content/developer/integration/reverse-proxy/haproxy.md @@ -0,0 +1,234 @@ +--- +title: "HAProxy" +description: "Deploy RustFS behind HAProxy with separate HTTPS routes for the S3 API and Console." +--- + +Use **HAProxy** to terminate TLS and route separate hostnames to the RustFS S3 API and Console. This deployment runs HAProxy and a single-node RustFS instance with Docker Compose. You need Docker Engine, Docker Compose, two DNS records, and a TLS certificate that covers both hostnames. + +This guide uses these example hostnames: + +- `s3.example.com` for the S3 API +- `console.example.com` for the Console + +Replace them with hostnames that resolve to the Docker host. + +:::warning[Serve S3 from the root path] + +Do not publish the S3 API under a path such as `/s3/`. AWS Signature Version 4 includes the request path and host, so rewriting either value can invalidate signed requests. + +::: + +## 1. Create the deployment directories + +Create directories for the HAProxy configuration and TLS certificate: + +```bash +mkdir -p rustfs-haproxy/config rustfs-haproxy/certs +cd rustfs-haproxy +``` + +HAProxy expects the certificate chain and private key in one PEM file. Combine them in this order: + +```bash +cat fullchain.pem privkey.pem > certs/rustfs.pem +chmod 600 certs/rustfs.pem +``` + +The certificate must cover both public hostnames. + +## 2. Set RustFS credentials + +Create an environment file and replace both credential placeholders: + +```ini title=".env" +RUSTFS_ACCESS_KEY= +RUSTFS_SECRET_KEY= +``` + +Do not commit this file or the certificate private key to source control. + +## 3. Configure HAProxy + +Create the HAProxy configuration: + +```text title="config/haproxy.cfg" +global + log stdout format raw local0 + +defaults + log global + mode http + option httplog + timeout connect 10s + timeout client 1h + timeout server 1h + timeout http-request 30s + timeout tunnel 1h + +frontend http + bind :80 + http-request redirect scheme https code 301 + +frontend https + bind :443 ssl crt /usr/local/etc/haproxy/certs/rustfs.pem alpn h2,http/1.1 + + acl host_s3 hdr(host) -i s3.example.com + acl host_console hdr(host) -i console.example.com + + use_backend rustfs_s3 if host_s3 + use_backend rustfs_console if host_console + default_backend reject_unknown_host + +backend reject_unknown_host + http-request deny deny_status 404 + +backend rustfs_s3 + balance leastconn + option httpchk GET /health/ready + http-check expect status 200 + server rustfs rustfs:9000 check inter 10s fall 3 rise 2 + +backend rustfs_console + balance leastconn + cookie RUSTFS_CONSOLE insert indirect nocache secure httponly + option httpchk GET /rustfs/console/health + http-check expect status 200 + server rustfs rustfs:9001 check inter 10s fall 3 rise 2 cookie rustfs +``` + +HAProxy preserves the incoming host and request path unless you explicitly rewrite them. The long client, server, and tunnel timeouts accommodate streaming S3 operations and Console WebSocket connections. + +The Console backend sets an affinity cookie. With one RustFS server it has no routing effect, but keeping it in the base configuration makes the behavior consistent when you add nodes. + +## 4. Create the Compose file + +Create the HAProxy and RustFS services: + +```yaml title="compose.yaml" +services: + haproxy: + image: haproxy:3.2-alpine + restart: unless-stopped + depends_on: + rustfs: + condition: service_healthy + ports: + - "80:80" + - "443:443" + volumes: + - ./config/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro + - ./certs:/usr/local/etc/haproxy/certs:ro + networks: + - rustfs + + rustfs: + image: rustfs/rustfs:latest + restart: unless-stopped + environment: + RUSTFS_ACCESS_KEY: ${RUSTFS_ACCESS_KEY} + RUSTFS_SECRET_KEY: ${RUSTFS_SECRET_KEY} + RUSTFS_CONSOLE_ENABLE: "true" + RUSTFS_ADDRESS: "0.0.0.0:9000" + RUSTFS_CONSOLE_ADDRESS: "0.0.0.0:9001" + expose: + - "9000" + - "9001" + volumes: + - rustfs-data:/data + healthcheck: + test: ["CMD-SHELL", "curl --fail http://127.0.0.1:9000/health/ready && curl --fail http://127.0.0.1:9001/rustfs/console/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + networks: + - rustfs + +volumes: + rustfs-data: + +networks: + rustfs: +``` + +Only HAProxy publishes host ports. RustFS ports `9000` and `9001` remain reachable inside the Compose network. + +## 5. Validate and start the deployment + +Render the Compose configuration and start RustFS: + +```bash +docker compose config +docker compose up -d rustfs +``` + +Validate the HAProxy configuration with the same image used by the deployment: + +```bash +docker compose run --rm --no-deps haproxy haproxy -c -f /usr/local/etc/haproxy/haproxy.cfg +``` + +Start HAProxy and check both services: + +```bash +docker compose up -d haproxy +docker compose ps +``` + +If a service does not become healthy, inspect its logs: + +```bash +docker compose logs haproxy +docker compose logs rustfs +``` + +## 6. Verify both endpoints + +Verify the API and Console through their public HTTPS hostnames: + +```bash +curl --fail https://s3.example.com/health/ready +curl --fail https://console.example.com/rustfs/console/health +``` + +Configure S3 clients with `https://s3.example.com` as the endpoint and enable path-style addressing. Open `https://console.example.com` to sign in to the Console. + +When you replace a renewed `certs/rustfs.pem`, validate the configuration and recreate the HAProxy container to load it: + +```bash +docker compose run --rm --no-deps haproxy haproxy -c -f /usr/local/etc/haproxy/haproxy.cfg +docker compose up -d --force-recreate haproxy +``` + +## Multi-node backends + +For a distributed RustFS deployment, add every RustFS node to both backends: + +```text title="config/haproxy.cfg" +backend rustfs_s3 + balance leastconn + option httpchk GET /health/ready + http-check expect status 200 + server node1 node1.example.net:9000 check inter 10s fall 3 rise 2 + server node2 node2.example.net:9000 check inter 10s fall 3 rise 2 + server node3 node3.example.net:9000 check inter 10s fall 3 rise 2 + server node4 node4.example.net:9000 check inter 10s fall 3 rise 2 + +backend rustfs_console + balance leastconn + cookie RUSTFS_CONSOLE insert indirect nocache secure httponly + option httpchk GET /rustfs/console/health + http-check expect status 200 + server node1 node1.example.net:9001 check inter 10s fall 3 rise 2 cookie node1 + server node2 node2.example.net:9001 check inter 10s fall 3 rise 2 cookie node2 + server node3 node3.example.net:9001 check inter 10s fall 3 rise 2 cookie node3 + server node4 node4.example.net:9001 check inter 10s fall 3 rise 2 cookie node4 +``` + +The Console cookie keeps an in-progress OpenID Connect login on the RustFS node that created its `state`. Keep port `9000` open directly between RustFS nodes because internal node RPC uses the same listener. + +## Next steps + +- [Configure an S3 client](/developer/examples/aws-cli) +- [Enable virtual-hosted-style bucket URLs](/integration/virtual) +- [Review health and readiness endpoints](/operations/status-check) \ No newline at end of file diff --git a/content/developer/integration/reverse-proxy/index.md b/content/developer/integration/reverse-proxy/index.md new file mode 100644 index 00000000..d7b0c617 --- /dev/null +++ b/content/developer/integration/reverse-proxy/index.md @@ -0,0 +1,19 @@ +--- +title: "Reverse Proxy" +description: "Choose and configure a reverse proxy for the RustFS S3 API and Console." +--- + +Use a reverse proxy to expose the **RustFS** S3 API and Console through managed hostnames and TLS endpoints. + +We recommend using separate hostnames for the S3 API on port `9000` and the Console on port `9001`. Serve the S3 API from the root of its hostname because S3 clients sign the request path. + +## Supported guides + +- [Nginx](./nginx.md) +- [Traefik](./traefik.md) +- [Caddy](./caddy.md) +- [HAProxy](./haproxy.md) + +## Related configuration + +See [Virtual-Host Access](/integration/virtual) when clients access buckets through virtual-hosted-style URLs. \ No newline at end of file diff --git a/content/developer/integration/reverse-proxy/meta.json b/content/developer/integration/reverse-proxy/meta.json new file mode 100644 index 00000000..ea7175e0 --- /dev/null +++ b/content/developer/integration/reverse-proxy/meta.json @@ -0,0 +1,9 @@ +{ + "title": "Reverse Proxy", + "pages": [ + "nginx", + "traefik", + "caddy", + "haproxy" + ] +} \ No newline at end of file diff --git a/content/developer/integration/reverse-proxy/nginx.md b/content/developer/integration/reverse-proxy/nginx.md new file mode 100644 index 00000000..12eb5230 --- /dev/null +++ b/content/developer/integration/reverse-proxy/nginx.md @@ -0,0 +1,267 @@ +--- +title: "Nginx" +description: "Deploy RustFS behind Nginx with separate HTTPS endpoints for the S3 API and Console." +--- + +Use **Nginx** to terminate TLS and route separate hostnames to the RustFS S3 API and Console. This deployment runs Nginx and a single-node RustFS instance with Docker Compose. You need Docker Engine, Docker Compose, two DNS records, and a TLS certificate that covers both hostnames. + +This guide uses these example hostnames: + +- `s3.example.com` for the S3 API +- `console.example.com` for the Console + +Replace them with hostnames that resolve to the Docker host. + +:::warning[Serve S3 from the root path] + +Do not publish the S3 API under a path such as `/s3/`. AWS Signature Version 4 includes the request path and host, so rewriting either value can invalidate signed requests. + +::: + +## 1. Create the deployment directories + +Create directories for the Nginx configuration and TLS certificate: + +```bash +mkdir -p rustfs-nginx/sites rustfs-nginx/certs +cd rustfs-nginx +``` + +Copy your certificate chain and private key into `certs/`: + +```text +rustfs-nginx/ +├── certs/ +│ ├── fullchain.pem +│ └── privkey.pem +└── sites/ +``` + +Restrict access to the private key: + +```bash +chmod 600 certs/privkey.pem +``` + +## 2. Set RustFS credentials + +Create an environment file and replace both credential placeholders: + +```ini title=".env" +RUSTFS_ACCESS_KEY= +RUSTFS_SECRET_KEY= +``` + +Do not commit this file to source control. + +## 3. Configure Nginx + +Create the Nginx configuration: + +```nginx title="sites/rustfs.conf" +map $http_upgrade $connection_upgrade { + default upgrade; + '' ''; +} + +upstream rustfs_s3 { + server rustfs:9000; + keepalive 32; +} + +upstream rustfs_console { + server rustfs:9001; + keepalive 16; +} + +server { + listen 80; + listen [::]:80; + server_name s3.example.com console.example.com; + + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + server_name s3.example.com; + + ssl_certificate /etc/nginx/certs/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/privkey.pem; + + ignore_invalid_headers off; + client_max_body_size 0; + proxy_buffering off; + proxy_request_buffering off; + + location / { + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Connection ''; + proxy_cache_convert_head off; + proxy_connect_timeout 300s; + chunked_transfer_encoding off; + proxy_pass http://rustfs_s3; + } +} + +server { + listen 443 ssl; + listen [::]:443 ssl; + http2 on; + server_name console.example.com; + + ssl_certificate /etc/nginx/certs/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/privkey.pem; + + client_max_body_size 0; + proxy_buffering off; + proxy_request_buffering off; + + location / { + proxy_http_version 1.1; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_connect_timeout 300s; + proxy_pass http://rustfs_console; + } +} +``` + +The S3 server preserves the original host and request path, disables request buffering for streaming uploads, and does not convert signed `HEAD` requests. The Console server also forwards WebSocket upgrade headers. + +## 4. Create the Compose file + +Create the deployment definition: + +```yaml title="compose.yaml" +services: + nginx: + image: nginx:1.25-alpine + restart: unless-stopped + depends_on: + rustfs: + condition: service_healthy + ports: + - "80:80" + - "443:443" + volumes: + - ./sites/rustfs.conf:/etc/nginx/conf.d/rustfs.conf:ro + - ./certs:/etc/nginx/certs:ro + networks: + - rustfs + + rustfs: + image: rustfs/rustfs:latest + restart: unless-stopped + environment: + RUSTFS_ACCESS_KEY: ${RUSTFS_ACCESS_KEY} + RUSTFS_SECRET_KEY: ${RUSTFS_SECRET_KEY} + RUSTFS_CONSOLE_ENABLE: "true" + RUSTFS_ADDRESS: "0.0.0.0:9000" + RUSTFS_CONSOLE_ADDRESS: "0.0.0.0:9001" + expose: + - "9000" + - "9001" + volumes: + - rustfs-data:/data + healthcheck: + test: ["CMD-SHELL", "curl --fail http://127.0.0.1:9000/health/ready && curl --fail http://127.0.0.1:9001/rustfs/console/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + networks: + - rustfs + +volumes: + rustfs-data: + +networks: + rustfs: +``` + +Only Nginx publishes host ports. RustFS ports `9000` and `9001` remain reachable inside the Compose network. + +## 5. Validate and start the deployment + +Validate both configuration files before starting the services: + +```bash +docker compose config +docker compose up -d rustfs +docker compose run --rm --no-deps nginx nginx -t +``` + +Start Nginx and check both services: + +```bash +docker compose up -d nginx +docker compose ps +``` + +If a service does not become healthy, inspect its logs: + +```bash +docker compose logs nginx +docker compose logs rustfs +``` + +## 6. Verify both endpoints + +Verify the API and Console through their public HTTPS hostnames: + +```bash +curl --fail https://s3.example.com/health/ready +curl --fail https://console.example.com/rustfs/console/health +``` + +Configure S3 clients with `https://s3.example.com` as the endpoint and enable path-style addressing. Open `https://console.example.com` to sign in to the Console. + +When you replace a renewed certificate or key in `certs/`, validate and reload Nginx without interrupting active connections: + +```bash +docker compose exec nginx nginx -t +docker compose exec nginx nginx -s reload +``` + +## Multi-node upstreams + +For a distributed RustFS deployment, replace the single server in each upstream with all RustFS nodes: + +```nginx title="sites/rustfs.conf" +upstream rustfs_s3 { + least_conn; + server node1.example.net:9000; + server node2.example.net:9000; + server node3.example.net:9000; + server node4.example.net:9000; + keepalive 32; +} + +upstream rustfs_console { + ip_hash; + server node1.example.net:9001; + server node2.example.net:9001; + server node3.example.net:9001; + server node4.example.net:9001; + keepalive 16; +} +``` + +The Console upstream uses client affinity because an in-progress OpenID Connect login stores its `state` on one RustFS node. Keep port `9000` open between RustFS nodes because internal node RPC uses the same listener. + +## Next steps + +- [Configure an S3 client](/developer/examples/aws-cli) +- [Enable virtual-hosted-style bucket URLs](/integration/virtual) +- [Review health and readiness endpoints](/operations/status-check) \ No newline at end of file diff --git a/content/developer/integration/reverse-proxy/traefik.md b/content/developer/integration/reverse-proxy/traefik.md new file mode 100644 index 00000000..7e319593 --- /dev/null +++ b/content/developer/integration/reverse-proxy/traefik.md @@ -0,0 +1,241 @@ +--- +title: "Traefik" +description: "Deploy RustFS behind Traefik with automatic TLS for separate S3 API and Console hostnames." +--- + +Use **Traefik** and its Docker provider to discover RustFS, obtain TLS certificates from Let's Encrypt, and route separate hostnames to the S3 API and Console. You need Docker Engine, Docker Compose, a public server, and two DNS records that resolve to that server. + +This guide uses these example hostnames: + +- `s3.example.com` for the S3 API +- `console.example.com` for the Console + +Replace them with your public hostnames. The ACME HTTP-01 challenge requires inbound access to ports `80` and `443`. + +:::warning[Serve S3 from the root path] + +Do not publish the S3 API under a path such as `/s3/`. AWS Signature Version 4 includes the request path and host, so rewriting either value can invalidate signed requests. + +::: + +## 1. Create the deployment directory + +Create a directory and an empty ACME storage file. Traefik requires restrictive permissions on this file: + +```bash +mkdir rustfs-traefik +cd rustfs-traefik +touch acme.json +chmod 600 acme.json +``` + +## 2. Set deployment variables + +Create an environment file and replace each value: + +```ini title=".env" +S3_HOSTNAME=s3.example.com +CONSOLE_HOSTNAME=console.example.com +ACME_EMAIL=admin@example.com +RUSTFS_ACCESS_KEY= +RUSTFS_SECRET_KEY= +``` + +Use an email address that receives certificate expiration notices. Do not commit `.env` or `acme.json` to source control. + +## 3. Create the Compose file + +Create the Traefik and RustFS services: + +```yaml title="compose.yaml" +services: + traefik: + image: traefik:v3.6.5 + restart: unless-stopped + command: + - --log.level=INFO + - --accesslog=true + - --providers.docker=true + - --providers.docker.endpoint=unix:///var/run/docker.sock + - --providers.docker.exposedbydefault=false + - --providers.docker.network=rustfs + - --entrypoints.web.address=:80 + - --entrypoints.websecure.address=:443 + - --entrypoints.web.http.redirections.entrypoint.to=websecure + - --entrypoints.web.http.redirections.entrypoint.scheme=https + - --certificatesresolvers.le.acme.email=${ACME_EMAIL} + - --certificatesresolvers.le.acme.storage=/etc/traefik/acme.json + - --certificatesresolvers.le.acme.httpchallenge=true + - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web + ports: + - "80:80" + - "443:443" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./acme.json:/etc/traefik/acme.json + networks: + - rustfs + + rustfs: + image: rustfs/rustfs:latest + restart: unless-stopped + environment: + RUSTFS_ACCESS_KEY: ${RUSTFS_ACCESS_KEY} + RUSTFS_SECRET_KEY: ${RUSTFS_SECRET_KEY} + RUSTFS_CONSOLE_ENABLE: "true" + RUSTFS_ADDRESS: "0.0.0.0:9000" + RUSTFS_CONSOLE_ADDRESS: "0.0.0.0:9001" + expose: + - "9000" + - "9001" + volumes: + - rustfs-data:/data + healthcheck: + test: ["CMD-SHELL", "curl --fail http://127.0.0.1:9000/health/ready && curl --fail http://127.0.0.1:9001/rustfs/console/health"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + labels: + - traefik.enable=true + - traefik.docker.network=rustfs + - traefik.http.routers.rustfs-s3.rule=Host(`${S3_HOSTNAME}`) + - traefik.http.routers.rustfs-s3.entrypoints=websecure + - traefik.http.routers.rustfs-s3.tls=true + - traefik.http.routers.rustfs-s3.tls.certresolver=le + - traefik.http.routers.rustfs-s3.service=rustfs-s3 + - traefik.http.services.rustfs-s3.loadbalancer.server.port=9000 + - traefik.http.services.rustfs-s3.loadbalancer.passhostheader=true + - traefik.http.routers.rustfs-console.rule=Host(`${CONSOLE_HOSTNAME}`) + - traefik.http.routers.rustfs-console.entrypoints=websecure + - traefik.http.routers.rustfs-console.tls=true + - traefik.http.routers.rustfs-console.tls.certresolver=le + - traefik.http.routers.rustfs-console.service=rustfs-console + - traefik.http.services.rustfs-console.loadbalancer.server.port=9001 + - traefik.http.services.rustfs-console.loadbalancer.passhostheader=true + networks: + - rustfs + +volumes: + rustfs-data: + +networks: + rustfs: + name: rustfs +``` + +The two routers use different host rules and backend ports. RustFS does not publish ports `9000` or `9001` on the Docker host, and the Traefik Dashboard is not exposed. + +:::note[Docker socket access] + +Traefik reads container labels through the read-only Docker socket mount. Anyone who can modify Docker workloads can influence routes discovered by the Docker provider. Restrict Docker access on the proxy host. + +::: + +## 4. Validate and start the deployment + +Render the Compose configuration and check that all variables resolve: + +```bash +docker compose config +``` + +Start both services: + +```bash +docker compose up -d +docker compose ps +``` + +Follow the Traefik logs while it completes the ACME challenge and creates both certificates: + +```bash +docker compose logs --follow traefik +``` + +If certificate issuance fails, confirm that both DNS records resolve to this host and that ports `80` and `443` are reachable from the internet. Let's Encrypt rate limits apply, so correct DNS and firewall problems before repeatedly recreating the deployment. + +## 5. Verify both endpoints + +Verify the S3 API readiness endpoint and Console health endpoint through Traefik: + +```bash +curl --fail https://s3.example.com/health/ready +curl --fail https://console.example.com/rustfs/console/health +``` + +Configure S3 clients with `https://s3.example.com` as the endpoint and enable path-style addressing. Open `https://console.example.com` to sign in to the Console. + +## Multi-node services + +For an external multi-node RustFS cluster, enable Traefik's file provider in the `traefik` service: + +```yaml title="compose.yaml" +services: + traefik: + command: + - --providers.file.filename=/etc/traefik/dynamic.yaml + - --providers.file.watch=true + volumes: + - ./dynamic.yaml:/etc/traefik/dynamic.yaml:ro +``` + +Create the dynamic configuration with every RustFS node: + +```yaml title="dynamic.yaml" +http: + routers: + rustfs-s3: + rule: Host(`s3.example.com`) + entryPoints: + - websecure + service: rustfs-s3 + tls: + certResolver: le + rustfs-console: + rule: Host(`console.example.com`) + entryPoints: + - websecure + service: rustfs-console + tls: + certResolver: le + + services: + rustfs-s3: + loadBalancer: + passHostHeader: true + healthCheck: + path: /health/ready + interval: 10s + timeout: 5s + servers: + - url: http://node1.example.net:9000 + - url: http://node2.example.net:9000 + - url: http://node3.example.net:9000 + - url: http://node4.example.net:9000 + rustfs-console: + loadBalancer: + passHostHeader: true + sticky: + cookie: + name: rustfs_console + secure: true + httpOnly: true + healthCheck: + path: /rustfs/console/health + interval: 10s + timeout: 5s + servers: + - url: http://node1.example.net:9001 + - url: http://node2.example.net:9001 + - url: http://node3.example.net:9001 + - url: http://node4.example.net:9001 +``` + +Configure sticky sessions for the Console service when you use OpenID Connect. An in-progress login stores its `state` on one RustFS node and the callback must return to that node. Keep port `9000` open directly between RustFS nodes because internal node RPC uses the same listener. + +## Next steps + +- [Configure an S3 client](/developer/examples/aws-cli) +- [Enable virtual-hosted-style bucket URLs](/integration/virtual) +- [Review health and readiness endpoints](/operations/status-check) \ No newline at end of file diff --git a/content/developer/mc.md b/content/developer/mc.md deleted file mode 100644 index 62a9d4c5..00000000 --- a/content/developer/mc.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: "Manage RustFS Objects with MinIO Client" -description: "Manage RustFS objects using the MinIO Client." ---- - -The MinIO Client (`mc`) is a command-line tool for managing object storage services. It supports MinIO, Amazon S3, and other S3-compatible services. As RustFS is S3-compatible, you can use `mc` to manage RustFS objects. - -Prerequisites: - -- An available RustFS instance. Refer to the [Installation Guide](../installation/index.md). -- The `mc` tool is installed. -- Available [access keys](../administration/iam/access-token.md). - -## Using `mc` with RustFS - -First, configure an alias for RustFS using the `mc alias` command: - -```bash -mc alias set rustfs http://192.168.1.100:9000 -``` - -Output: - -```bash -Added `rustfs` successfully. -``` - -You can now use `mc` with the `rustfs` alias to create/delete buckets, upload/download files, etc. - -### List Buckets - -List all buckets: - -```bash -mc ls rustfs -``` - -Output: - -```bash -[2025-08-01 10:46:24 CST] 0B bucket-creation-by-api/ -[2025-07-29 09:15:35 CST] 0B rustfs-demo/ -[2025-08-03 09:44:45 CST] 0B bucket-creation-by-ui/ -``` - -### Create Bucket - -Create a bucket: - -```bash -mc mb rustfs/bucket-creation-by-mc -``` - -Output: - -```bash -Bucket created successfully `rustfs/bucket-creation-by-mc`. -``` - -### Delete Bucket - -Delete a bucket: - -```bash -mc rb rustfs/bucket-creation-by-mc -``` - -Output: - -```bash -Removed `rustfs/bucket-creation-by-mc` successfully. -``` - -### Upload File - -Upload a file to a bucket: - -```bash -mc cp file_name rustfs/bucket-creation-by-mc -``` - -Output: - -```bash -...path/to/file_name: 4 B / 4 B ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 12 B/s 0s% -``` - -### Delete File - -Delete a file from a bucket: - -```bash -mc rm rustfs/bucket-creation-by-mc/file_name -``` - -Output: - -```bash -Removed `rustfs/bucket-creation-by-mc/1.txt`. -``` - -### Download File from Storage - -Use `mc get` command to download a file from a bucket: - -```bash -mc get rustfs/bucket-creation-by-mc/file_name ./file_name -``` - -Response: - -```bash -...eation-by-mc/file_name: 4 B / 4 B ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ 18 B/s 0s% -``` diff --git a/content/developer/mcp.md b/content/developer/mcp.md index 3333db3e..fd997b01 100644 --- a/content/developer/mcp.md +++ b/content/developer/mcp.md @@ -174,7 +174,7 @@ Currently, mainstream AI IDEs such as Cursor, Windsurf, Trae, etc. all support M } ``` -> `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` are RustFS access keys. You can refer to the [Access Key Management chapter](../administration/iam/access-token.md) for creation. +> `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` are RustFS access keys. You can refer to the [Access Key Management chapter](../security-compliance/iam/access-token.md) for creation. If added successfully, you can list the [available tools](#️-available-tools) on the MCP configuration page. diff --git a/content/developer/meta.json b/content/developer/meta.json new file mode 100644 index 00000000..0e098da0 --- /dev/null +++ b/content/developer/meta.json @@ -0,0 +1,9 @@ +{ + "title": "Developer", + "defaultOpen": false, + "pages": [ + "sdk", + "integration", + "license" + ] +} \ No newline at end of file diff --git a/content/developer/sdk/go.md b/content/developer/sdk/go.md index 16bd0542..b40cab27 100644 --- a/content/developer/sdk/go.md +++ b/content/developer/sdk/go.md @@ -9,7 +9,7 @@ RustFS ships no first-party Go SDK — it is fully S3-compatible, so you use the - Go 1.21 or later - A working RustFS instance (refer to [Installation Guide](../../installation/index.md)) — the S3 API listens on port `9000`, the Console on port `9001` -- Access keys, set at install time via the `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY` environment variables (refer to [Access Key Management](../../administration/iam/access-token.md)) +- Access keys, set at install time via the `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY` environment variables (refer to [Access Key Management](../../security-compliance/iam/access-token.md)) :::tip[Local test] diff --git a/content/developer/sdk/java.md b/content/developer/sdk/java.md index a0f307b9..938a7f16 100644 --- a/content/developer/sdk/java.md +++ b/content/developer/sdk/java.md @@ -9,7 +9,7 @@ RustFS ships no first-party Java SDK — it is S3-compatible, so you use the off * Java 8 or later and Maven (or Gradle) * A running RustFS instance (see the [Installation Guide](../../installation/index.md)) — the S3 API listens on port `9000`, the Console on port `9001` -* Access keys, set at install time via the `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY` environment variables (see [Access Key Management](../../administration/iam/access-token.md)) +* Access keys, set at install time via the `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY` environment variables (see [Access Key Management](../../security-compliance/iam/access-token.md)) :::tip[Local test] diff --git a/content/developer/sdk/javascript.md b/content/developer/sdk/javascript.md index b9ab2914..f010b46f 100644 --- a/content/developer/sdk/javascript.md +++ b/content/developer/sdk/javascript.md @@ -11,7 +11,7 @@ RustFS ships no first-party JavaScript SDK — it is S3-compatible, so you use t * Node.js 18 or later * A running RustFS instance (see the [Installation Guide](../../installation/index.md)) — the S3 API listens on port `9000`, the Console on port `9001` -* Access keys, set at install time via the `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY` environment variables (see [Access Key Management](../../administration/iam/access-token.md)) +* Access keys, set at install time via the `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY` environment variables (see [Access Key Management](../../security-compliance/iam/access-token.md)) :::tip[Local test] diff --git a/content/developer/sdk/python.md b/content/developer/sdk/python.md index c6c63133..922ec994 100644 --- a/content/developer/sdk/python.md +++ b/content/developer/sdk/python.md @@ -21,7 +21,7 @@ This guide covers: * Python 3.8 or later * A running RustFS instance (see the [Installation Guide](../../installation/index.md)) — the S3 API listens on port `9000`, the Console on port `9001` -* Access keys, set at install time via the `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY` environment variables (see [Access Key Management](../../administration/iam/access-token.md)) +* Access keys, set at install time via the `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY` environment variables (see [Access Key Management](../../security-compliance/iam/access-token.md)) :::tip[Local test] diff --git a/content/developer/sdk/rust.md b/content/developer/sdk/rust.md index 746f7df2..9d818134 100644 --- a/content/developer/sdk/rust.md +++ b/content/developer/sdk/rust.md @@ -9,7 +9,7 @@ RustFS ships no first-party Rust client crate — it is fully S3-compatible, so - Rust 1.78 or later (install via [rustup](https://rustup.rs/)) - An available RustFS instance (refer to [Installation Guide](../../installation/index.md)) — the S3 API listens on port `9000`, the Console on port `9001` -- Access keys, set at install time via the `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY` environment variables (refer to [Access Key Management](../../administration/iam/access-token.md)) +- Access keys, set at install time via the `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY` environment variables (refer to [Access Key Management](../../security-compliance/iam/access-token.md)) :::tip[Local test] diff --git a/content/developer/sdk/typescript.md b/content/developer/sdk/typescript.md index 5218f01c..d26b7a93 100644 --- a/content/developer/sdk/typescript.md +++ b/content/developer/sdk/typescript.md @@ -9,7 +9,7 @@ RustFS ships no first-party TypeScript SDK — it is fully S3-compatible, so you - Node.js 18 or later (the examples use ES modules — set `"type": "module"` in your `package.json`) - An available RustFS instance (refer to [Installation Guide](../../installation/index.md) for installation) — the S3 API listens on port `9000`, the Console on port `9001` -- Access keys, set at install time via the `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY` environment variables (refer to [Access Key Management](../../administration/iam/access-token.md) for creation) +- Access keys, set at install time via the `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY` environment variables (refer to [Access Key Management](../../security-compliance/iam/access-token.md) for creation) :::tip[Local test] diff --git a/content/index.mdx b/content/index.mdx index 140242e3..d9b0e2a3 100644 --- a/content/index.mdx +++ b/content/index.mdx @@ -1,69 +1,83 @@ --- title: "RustFS Documentation" -description: "RustFS is a high-performance, distributed, S3-compatible object storage system written in Rust." +description: "Install RustFS on Linux, containers, Kubernetes, macOS, or Windows." --- -RustFS is a high-performance, distributed object storage system written in Rust, fully compatible with the S3 protocol and released under the Apache 2.0 license. It suits private cloud, data lake, AI/ML, and cloud-native workloads. +
+
+
+ RustFS is a distributed object storage system written in Rust and built + for S3-compatible workloads. This documentation covers installation, + administration, security, and operations for RustFS clusters. +
+ + Review installation requirements + +
-## Try RustFS in 10 minutes +
+ + + + Install on Linux + Start on a Linux server, then choose a single-node or distributed topology. + + - - } title="Quick Start" href="/installation/linux/quick-start"> - One command to install, log in to the Console, and store your first object. - - } title="Run with Docker" href="/installation/docker"> - A single `docker run` for local evaluation — no server required. - - + + + + Install with Docker + Run RustFS as a container for local evaluation or managed deployments. + + -## Find your path + + + + Install with Podman + Deploy a rootless or daemonless RustFS container with Podman. + + -### Evaluating RustFS? + + + + Install with Helm + Deploy one RustFS cluster on Kubernetes with the RustFS Helm chart. + + - - } title="What is RustFS?" href="/concepts/introduction"> - Positioning, features, and license. - - } title="Architecture & Comparison" href="/concepts/architecture"> - How it works and how it measures up against alternatives. - - } title="Usage Limits" href="/concepts/limit"> - The boundaries you should know before committing. - - + + + + Install with the Operator + Manage Kubernetes-native RustFS tenants, pools, and lifecycle operations. + + -### Deploying and operating? + + + + Install on macOS + Run RustFS locally on Apple silicon or Intel-based Mac computers. + + - - } title="Installation Guide" href="/installation/"> - Linux (single-node to multi-node), Docker, Kubernetes, Windows, macOS. - - } title="Production Checklists" href="/installation/checklists/hardware-checklists"> - Hardware, network, software, and security prep. - - } title="Operations" href="/upgrade-scale/upgrade"> - Rolling upgrades, scaling, troubleshooting, and data healing. - - - -### Building an application? - - - } title="SDKs" href="/developer/sdk"> - Java, Python, Go, Rust, JavaScript/TypeScript — any S3 client works. - - } title="Tool Examples" href="/developer/examples/mc"> - Minimal recipes for mc, AWS CLI, boto3, and rclone. - - } title="Reference" href="/reference/environment-variables"> - Environment variables, CLI, ports, limits, and glossary. - - - -## Why RustFS - -- **S3 Compatible** — drop-in for the S3 ecosystem: SDKs, tools, and data pipelines. -- **Distributed** — erasure-coded storage pools scale across disks and nodes. -- **Fast and memory-safe** — Rust performance without garbage-collection pauses. -- **Open source, commercially friendly** — Apache 2.0 licensed. -- **Cloud-native** — first-class Docker and Kubernetes deployment. + + + + Install on Windows + Install and run RustFS on a supported Windows environment. + + +
+
diff --git a/content/installation/checklists/meta.json b/content/installation/checklists/meta.json deleted file mode 100644 index 97bdf2a8..00000000 --- a/content/installation/checklists/meta.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "Production Checklists", - "pages": [ - "[Hardware Checklist](/installation/checklists/hardware-checklists)", - "[Hardware Selection](/installation/checklists/hardware-selection)", - "[Network Checklist](/installation/checklists/network-checklists)", - "[Software Checklist](/installation/checklists/software-checklists)", - "[Security Checklist](/installation/checklists/security-checklists)" - ] -} diff --git a/content/installation/cloud-native/helm-chart/cert-manager.md b/content/installation/cloud-native/helm-chart/cert-manager.md new file mode 100644 index 00000000..5386eaf3 --- /dev/null +++ b/content/installation/cloud-native/helm-chart/cert-manager.md @@ -0,0 +1,69 @@ +--- +title: cert-manager +description: Use cert-manager to issue and renew TLS certificates for a RustFS Helm deployment. +--- + +Use **cert-manager** to issue the certificate referenced by the RustFS Ingress. This provides HTTPS for the public S3 API and Console endpoint and allows cert-manager to renew the certificate. + +## Requirements + +- cert-manager is installed and its controller Pods are ready. +- An `Issuer` or `ClusterIssuer` is ready. +- The RustFS hostname resolves to the Ingress controller. + +Verify the issuer before installing RustFS: + +```bash +kubectl get clusterissuer letsencrypt-prod +kubectl -n cert-manager get pods +``` + +## 1. Configure Ingress TLS + +Add the Ingress and certificate settings to your standalone or distributed values file: + +```yaml title="values.yaml" +ingress: + enabled: true + className: nginx + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + hosts: + - host: s3.example.com + paths: + - path: / + pathType: Prefix + tls: + enabled: true + certManager: + enabled: true + existingSecret: + enabled: false + name: "" +``` + +Replace the Ingress class, issuer, and hostname for your cluster. For a namespace-scoped Issuer, use the `cert-manager.io/issuer` annotation instead. + +## 2. Apply the configuration + +```bash +helm upgrade rustfs ./helm/rustfs \ + --namespace rustfs \ + -f values.yaml +``` + +The Ingress references the `rustfs-tls` Secret for a release named `rustfs`. cert-manager ingress-shim reads the issuer annotation and creates a Certificate that writes to this Secret. + +## 3. Verify the certificate + +```bash +kubectl -n rustfs get ingress,certificate,certificaterequest +kubectl -n rustfs describe certificate rustfs-tls +kubectl -n rustfs get secret rustfs-tls +``` + +Wait for the Certificate to report `Ready=True`, then open `https://s3.example.com`. + +## Use cert-manager with mTLS + +The chart also uses cert-manager to issue server and client certificates when `mtls.enabled=true`. See [mTLS](./mtls.md) to use the chart-managed CA or reference an existing issuer. \ No newline at end of file diff --git a/content/installation/cloud-native/helm-chart/index.md b/content/installation/cloud-native/helm-chart/index.md new file mode 100644 index 00000000..bc28bffe --- /dev/null +++ b/content/installation/cloud-native/helm-chart/index.md @@ -0,0 +1,21 @@ +--- +title: Overview +description: Understand how the RustFS Helm chart deploys standalone and distributed clusters on Kubernetes. +--- + +The official **RustFS Helm chart** deploys one RustFS cluster directly into Kubernetes. Helm renders the workload, Services, credentials, configuration, PersistentVolumeClaims (PVCs), Ingress, and optional certificate resources from a single values file. + +The chart supports two deployment modes: + +- **Standalone** creates one Pod with one data PVC. Use it for evaluation and development. +- **Distributed** creates a StatefulSet with multiple Pods and data PVCs. `replicaCount` controls the number of Pods and `drivesPerNode` controls the number of data PVCs mounted by each Pod. + +Distributed mode is enabled by default. The chart also supports multiple append-only server pools, but a single explicit topology is easier to operate for an initial deployment. Credentials must be supplied through chart values or an existing Secret; the chart rejects empty and well-known default credentials unless insecure development defaults are explicitly enabled. + +Use the Helm chart when you want Helm to manage one RustFS cluster. Use the [RustFS Operator](../operator/index.md) when you need Kubernetes custom resources, multiple Tenants, or Operator-driven pool management. + +## Helm chart workflows + +- [Install](./installation.md) covers requirements and standalone or distributed deployment. +- [mTLS](./mtls.md) encrypts and authenticates traffic between RustFS Pods. +- [cert-manager](./cert-manager.md) issues and renews certificates for RustFS Ingress and mTLS. \ No newline at end of file diff --git a/content/installation/cloud-native/helm-chart/installation.mdx b/content/installation/cloud-native/helm-chart/installation.mdx new file mode 100644 index 00000000..5523fffb --- /dev/null +++ b/content/installation/cloud-native/helm-chart/installation.mdx @@ -0,0 +1,168 @@ +--- +title: Install +description: Install standalone or distributed RustFS on Kubernetes with the official Helm chart. +--- + +This guide gets the official RustFS chart from source or the Helm repository, installs it, and verifies access to the S3 API and Console. + +## Requirements + +| Component | Requirement | +|-----------|-------------| +| Helm | Version 3 | +| Kubernetes | A cluster reachable with `kubectl` | +| StorageClass | Dynamic PVC provisioning for RustFS data | +| RustFS | `1.0.0-alpha.69` or later | + +An Ingress controller is optional. If you enable Ingress, set `ingress.className` to the controller in your cluster, such as `nginx` or `traefik`. + +Check the active cluster and available StorageClasses: + +```bash +kubectl config current-context +kubectl get storageclass +helm version --short +``` + +## 1. Get the chart + +Choose how Helm should access the chart. Both options set `RUSTFS_CHART`, which the installation commands below use. + + + + +Clone the RustFS source repository to use the chart at `helm/rustfs`: + +```bash +git clone https://github.com/rustfs/rustfs.git +cd rustfs +export RUSTFS_CHART=./helm/rustfs +``` + + + + +Add the RustFS repository listed on [Artifact Hub](https://artifacthub.io/packages/helm/rustfs/rustfs), then update the local repository index: + +```bash +helm repo add rustfs https://charts.rustfs.com +helm repo update +export RUSTFS_CHART=rustfs/rustfs +``` + + + + +## 2. Install standalone mode + +Create a values file for a one-Pod development deployment: + +```yaml title="standalone-values.yaml" +mode: + standalone: + enabled: true + distributed: + enabled: false + +secret: + rustfs: + access_key: "" + secret_key: "" + +storageclass: + name: standard + dataStorageSize: 10Gi + logStorageSize: 1Gi + +ingress: + enabled: false +``` + +Replace `standard` with a StorageClass in your cluster, then install: + +```bash +helm upgrade --install rustfs "$RUSTFS_CHART" \ + --namespace rustfs \ + --create-namespace \ + -f standalone-values.yaml +``` + +## 3. Install distributed mode + +For a distributed cluster, set the Pod and drive counts explicitly. Total data drives equal `replicaCount * drivesPerNode`. + +```yaml title="distributed-values.yaml" +mode: + standalone: + enabled: false + distributed: + enabled: true + +replicaCount: 4 +drivesPerNode: 2 + +secret: + rustfs: + access_key: "" + secret_key: "" + +storageclass: + name: standard + dataStorageSize: 100Gi + logStorageSize: 1Gi + +ingress: + enabled: false +``` + +The example creates four Pods and eight data PVCs. Ensure the cluster can schedule all Pods and provision all PVCs, then install: + +```bash +helm upgrade --install rustfs "$RUSTFS_CHART" \ + --namespace rustfs \ + --create-namespace \ + -f distributed-values.yaml +``` + +:::warning[Choose the topology before installation] + +Kubernetes does not allow updates to StatefulSet `volumeClaimTemplates`. Changing `drivesPerNode` later requires StatefulSet recreation or a new installation. + +::: + +## 4. Verify and access RustFS + +```bash +kubectl -n rustfs get pods,pvc,services +kubectl -n rustfs rollout status statefulset/rustfs +``` + +Standalone mode creates a Deployment instead of a StatefulSet. Check it with: + +```bash +kubectl -n rustfs rollout status deployment/rustfs +``` + +Forward the S3 API and Console to your workstation: + +```bash +kubectl -n rustfs port-forward svc/rustfs 9000:9000 9001:9001 +``` + +Use `http://localhost:9000` as the S3 endpoint and open `http://localhost:9001` for the Console. + +## Key values + +| Value | Purpose | Chart default | +|-------|---------|---------------| +| `mode.standalone.enabled` | Enable one-Pod standalone mode | `false` | +| `mode.distributed.enabled` | Enable distributed mode | `true` | +| `replicaCount` | Distributed Pod count | `4` | +| `drivesPerNode` | Data PVCs per Pod | Inferred from `replicaCount` | +| `storageclass.name` | StorageClass for PVCs | `local-path` | +| `storageclass.dataStorageSize` | Size of each data PVC | `256Mi` | +| `storageclass.logStorageSize` | Size of each log PVC | `256Mi` | +| `service.endpoint.port` | S3 API port | `9000` | +| `service.console.port` | Console port | `9001` | + +We recommend setting storage sizes explicitly; the chart defaults are intended only for basic evaluation. \ No newline at end of file diff --git a/content/installation/cloud-native/helm-chart/meta.json b/content/installation/cloud-native/helm-chart/meta.json new file mode 100644 index 00000000..c439f551 --- /dev/null +++ b/content/installation/cloud-native/helm-chart/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Helm Chart", + "pages": [ + "installation", + "mtls", + "cert-manager" + ] +} \ No newline at end of file diff --git a/content/installation/cloud-native/helm-chart/mtls.md b/content/installation/cloud-native/helm-chart/mtls.md new file mode 100644 index 00000000..5144b472 --- /dev/null +++ b/content/installation/cloud-native/helm-chart/mtls.md @@ -0,0 +1,66 @@ +--- +title: mTLS +description: Configure mutual TLS between RustFS Pods deployed by the Helm chart. +--- + +The chart can enable mutual Transport Layer Security (mTLS) for RustFS Pod communication. When enabled, RustFS requires client certificates, uses HTTPS for generated peer URLs, and mounts server, client, and CA material into every Pod. + +## Requirements + +mTLS uses cert-manager `Issuer` and `Certificate` resources. Install cert-manager before enabling it and confirm that its CRDs are available: + +```bash +kubectl get crd certificates.cert-manager.io issuers.cert-manager.io +``` + +## 1. Use the chart-managed CA + +Add the following setting to your existing values file: + +```yaml title="values.yaml" +mtls: + enabled: true +``` + +Upgrade the release: + +```bash +helm upgrade rustfs ./helm/rustfs \ + --namespace rustfs \ + -f values.yaml +``` + +The chart creates a self-signed root CA, a namespace Issuer, and server and client Certificates. It mounts the resulting Secrets and configures RustFS with `RUSTFS_SERVER_MTLS_ENABLE=1` and `RUSTFS_TLS_PATH=/opt/tls`. Health probes also use the generated client certificate. + +## 2. Use an existing Issuer + +To use an Issuer or ClusterIssuer already managed by your platform, configure its reference: + +```yaml title="values.yaml" +mtls: + enabled: true + existingIssuerRef: + enabled: true + name: internal-ca + kind: ClusterIssuer + group: cert-manager.io +``` + +The issuer must be ready and able to issue both server and client certificates in the `rustfs` namespace. Use `kind: Issuer` for a namespace-scoped issuer. + +## 3. Verify mTLS + +```bash +kubectl -n rustfs get issuer,certificate,secret +kubectl -n rustfs describe certificate rustfs-server-tls +kubectl -n rustfs describe certificate rustfs-client-tls +kubectl -n rustfs get pods +``` + +For a release named `rustfs`, the generated certificate Secrets are `rustfs-server-tls` and `rustfs-client-tls`. + +:::warning[Plan external access] + +mTLS requires clients to present a trusted certificate. Validate how your Ingress controller or other external client presents that certificate before enabling mTLS on an existing deployment. + +::: \ No newline at end of file diff --git a/content/installation/cloud-native/index.md b/content/installation/cloud-native/index.md index f55a2a6b..ef338b58 100644 --- a/content/installation/cloud-native/index.md +++ b/content/installation/cloud-native/index.md @@ -11,6 +11,7 @@ RustFS ships an official Helm chart that deploys either a single-node instance ( - Helm 3 - RustFS image version `>= 1.0.0-alpha.69` (the chart requirement) - A StorageClass with a working provisioner — the chart defaults to [`local-path`](https://github.com/rancher/local-path-provisioner); set `storageclass.name` to use your own +- [`rc`](/operations/rc) installed on the administration host before using the server-pool commands in this guide The chart lives in the RustFS source repository under `helm/rustfs`: @@ -146,7 +147,7 @@ Log in to the Console with the access key and secret key you set at install time ## 7. Scaling out with server pools -In distributed mode the chart can run multiple **server pools** — independent StatefulSets whose drives together form one cluster. This is the chart-level equivalent of adding a Server Pool as described in [Availability and Scalability](../../upgrade-scale/availability-and-resiliency.md). +In distributed mode the chart can run multiple **server pools** — independent StatefulSets whose drives together form one cluster. This is the chart-level equivalent of adding a Server Pool as described in [Pool Expansion](../../operations/scaling/storage-pool-expansion.md). To expand an existing deployment, enable pools and describe the current layout as pool 0 plus your new capacity: @@ -177,7 +178,7 @@ What to expect during the rollout, per the chart's documentation: :::note -`rc` is the RustFS admin command-line client referenced by the chart documentation (`rc admin pool ls` / `expand` / `rebalance` / `decommission`). Confirm its availability and packaging with your RustFS distribution before relying on it in runbooks. +`rc` is the RustFS command-line client. Use `rc admin pool list`, `expand`, `rebalance`, and `decommission` for the server-pool workflows described by the chart. ::: @@ -195,6 +196,6 @@ Helm does not delete PVCs created by StatefulSet volume claim templates. If you ## Next steps -- [Availability and Scalability](../../upgrade-scale/availability-and-resiliency.md) — how Server Pool expansion works at the cluster level -- [Upgrade](../../upgrade-scale/upgrade.md) — zero-downtime rolling upgrades +- [Pool Expansion](../../operations/scaling/storage-pool-expansion.md) — how Server Pool expansion works at the cluster level +- [Kubernetes Upgrade](../../operations/upgrade/kubernetes/index.md) — upgrade Helm- and Operator-managed deployments - [TLS configuration](../../integration/tls-configured.md) — end-to-end TLS options diff --git a/content/installation/cloud-native/meta.json b/content/installation/cloud-native/meta.json new file mode 100644 index 00000000..520b603a --- /dev/null +++ b/content/installation/cloud-native/meta.json @@ -0,0 +1,7 @@ +{ + "title": "Cloud Native", + "pages": [ + "helm-chart", + "operator" + ] +} diff --git a/content/installation/cloud-native/operator/index.md b/content/installation/cloud-native/operator/index.md new file mode 100644 index 00000000..68777f69 --- /dev/null +++ b/content/installation/cloud-native/operator/index.md @@ -0,0 +1,28 @@ +--- +title: Overview +description: Understand how RustFS Operator manages RustFS clusters on Kubernetes. +--- + +**RustFS Operator** applies the Kubernetes Operator pattern to RustFS clusters. Instead of creating StatefulSets, Services, PersistentVolumeClaims (PVCs), and configuration by hand, you declare the required storage cluster as a Kubernetes custom resource. The controller watches that resource and continuously reconciles the running cluster with the declared state. + +The Operator installs two Custom Resource Definitions (CRDs): + +- `Tenant` (`rustfs.com/v1alpha1`) represents one RustFS cluster. It defines storage pools, credentials, scheduling, Transport Layer Security (TLS), and Key Management Service (KMS) settings. +- `PolicyBinding` (`sts.rustfs.com/v1alpha1`) maps a Kubernetes ServiceAccount to RustFS policies when workloads request temporary credentials from the Operator Security Token Service (STS). + +One Operator can manage multiple Tenants across namespaces. Each Tenant has independent storage, credentials, S3 and Console services, and lifecycle. The Operator creates one StatefulSet for each pool, so you can add capacity by appending a pool without rebuilding the cluster. It also reports `Ready`, `Progressing`, or `Degraded` conditions and Kubernetes Events, and exposes health and metrics endpoints for cluster monitoring. + +The same API covers small test clusters and distributed deployments. Sensitive credentials and KMS material stay in Kubernetes Secrets, while version-controlled Tenant manifests hold only Secret references. This makes deployments repeatable, supports GitOps workflows, and keeps routine operations such as multi-tenant management, pool expansion, TLS, and encryption within Kubernetes-native tools. + +## Operator workflows + +- [Install](./installation.md) covers requirements, Helm installation, Console access, and TLS configuration. +- [Multi-Tenant](./tenant.md) creates isolated RustFS clusters for different teams or workloads. +- [Pool Expansion](./pool-expansion.md) adds storage capacity by appending a pool to an existing Tenant. +- [KMS Integration](./kms.md) configures local or HashiCorp Vault key management for encrypted data. + +:::warning[Pre-release software] + +RustFS Operator is currently `v0.1.0` pre-release software under active development. Validate upgrades and Tenant changes in a non-production cluster first. + +::: \ No newline at end of file diff --git a/content/installation/cloud-native/operator/installation.md b/content/installation/cloud-native/operator/installation.md new file mode 100644 index 00000000..778625e7 --- /dev/null +++ b/content/installation/cloud-native/operator/installation.md @@ -0,0 +1,119 @@ +--- +title: Install +description: Prepare Kubernetes, install RustFS Operator, and access its Console securely. +--- + +This guide installs the Operator with Helm, verifies the deployment, and exposes the Operator Console locally or through HTTPS. + +## Requirements + +| Component | Requirement | +|-----------|-------------| +| Kubernetes | `v1.30` or later | +| Helm | `v3.0` or later | +| kubectl | Compatible with the Kubernetes cluster | +| StorageClass | Dynamic PVC provisioning for Tenant storage | + +Your account must be able to create CRDs, cluster RBAC, Deployments, and Services. Confirm the target cluster before installation: + +```bash +kubectl config current-context +kubectl get storageclass +``` + +## 1. Install the Operator + +The Helm chart is stored in the Operator repository: + +```bash +git clone https://github.com/rustfs/operator.git +cd operator + +helm upgrade --install rustfs-operator deploy/rustfs-operator/ \ + --namespace rustfs-system \ + --create-namespace +``` + +Common settings belong in a values file: + +```yaml title="values.yaml" +operator: + replicas: 1 + metrics: + enabled: true + tenantMonitor: + enabled: true + intervalSeconds: 300 +console: + enabled: true + service: + type: ClusterIP +``` + +Apply the file with `-f values.yaml`. The chart generates `OPERATOR_*` variables from these values; do not duplicate them under `operator.env`. + +## 2. Verify the installation + +```bash +kubectl -n rustfs-system get pods,services +kubectl get crd tenants.rustfs.com +kubectl -n rustfs-system rollout status deployment/rustfs-operator +kubectl -n rustfs-system rollout status deployment/rustfs-operator-console +``` + +## 3. Access the Operator Console + +The Console listens on port `9090`. Generate a short-lived login token: + +```bash +kubectl -n rustfs-system create token rustfs-operator-console --duration=24h +``` + +Forward the Console service to your workstation: + +```bash +kubectl -n rustfs-system port-forward \ + svc/rustfs-operator-console 19090:9090 +``` + +Open `http://127.0.0.1:19090` and paste the token into the login form. The Helm installation notes print the exact ServiceAccount and Service names when release names or namespaces differ. + +:::note[Local HTTP access] + +If your browser does not retain the login over HTTP, set `CONSOLE_COOKIE_SECURE=false` under `console.env` for local testing only. Keep secure cookies enabled for HTTPS. + +::: + +## 4. Configure Console TLS + +Use one HTTPS hostname for both the Console UI and `/api/v1`. Create a TLS Secret, or let cert-manager create it, then enable Ingress: + +```yaml title="values.yaml" +console: + ingress: + enabled: true + className: nginx + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + hosts: + - host: console.example.com + paths: + - path: / + pathType: Prefix + tls: + - secretName: console-tls + hosts: + - console.example.com +``` + +Upgrade the release with the values file: + +```bash +helm upgrade rustfs-operator deploy/rustfs-operator/ \ + --namespace rustfs-system \ + -f values.yaml +``` + +Replace the Ingress class, issuer, and hostname for your environment. If cert-manager is not installed, create the `console-tls` Secret with your certificate and private key before the upgrade. + +Next, [create a Tenant](./tenant.md). \ No newline at end of file diff --git a/content/installation/cloud-native/operator/kms.md b/content/installation/cloud-native/operator/kms.md new file mode 100644 index 00000000..60c89950 --- /dev/null +++ b/content/installation/cloud-native/operator/kms.md @@ -0,0 +1,89 @@ +--- +title: KMS Integration +description: Configure local or HashiCorp Vault key management for a RustFS Tenant through the Operator encryption API. +--- + +Configure Key Management Service (KMS) integration through `spec.encryption`. Do not add `RUSTFS_KMS_*` variables to `spec.env`; the Operator generates them from the structured Tenant configuration and Secret references. + +## Choose a backend + +Use `local` only for a single-server Tenant. Use `vault` for distributed deployments where every Tenant Pod can reach HashiCorp Vault. + +## Local KMS + +Create a master key Secret: + +```yaml title="local-kms-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: rustfs-local-kms + namespace: storage-a +type: Opaque +stringData: + local-master-key: "replace-with-a-random-master-key" +``` + +Add the encryption block to the existing Tenant manifest: + +```yaml title="tenant.yaml" +spec: + encryption: + enabled: true + backend: local + local: + keyDirectory: /data/rustfs0/.kms-keys + masterKeySecretRef: + name: rustfs-local-kms + key: local-master-key + defaultKeyId: tenant-default +``` + +The key directory must be inside a mounted data path so it survives Pod replacement. + +## HashiCorp Vault KMS + +Create a Secret containing a Vault token: + +```yaml title="vault-kms-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: rustfs-kms + namespace: storage-a +type: Opaque +stringData: + vault-token: "replace-with-vault-token" +``` + +Add the Vault configuration to the existing Tenant manifest: + +```yaml title="tenant.yaml" +spec: + encryption: + enabled: true + backend: vault + vault: + endpoint: https://vault.example.com:8200 + kmsSecret: + name: rustfs-kms + defaultKeyId: tenant-default +``` + +Every Tenant Pod must be able to resolve and connect to the Vault endpoint and trust its certificate. + +## Apply the configuration + +```bash +kubectl apply -f local-kms-secret.yaml +kubectl apply -f tenant.yaml +kubectl -n storage-a describe tenant tenant-a +``` + +For Vault, apply `vault-kms-secret.yaml` before `tenant.yaml`. Changing encryption settings rolls the affected StatefulSets. + +:::warning[Protect encryption keys] + +Back up key material and test recovery before storing production data. Losing the local master key or Vault keys can make encrypted objects unrecoverable. + +::: \ No newline at end of file diff --git a/content/installation/cloud-native/operator/meta.json b/content/installation/cloud-native/operator/meta.json new file mode 100644 index 00000000..843952a8 --- /dev/null +++ b/content/installation/cloud-native/operator/meta.json @@ -0,0 +1,9 @@ +{ + "title": "Operator", + "pages": [ + "installation", + "tenant", + "pool-expansion", + "kms" + ] +} \ No newline at end of file diff --git a/content/installation/cloud-native/operator/pool-expansion.md b/content/installation/cloud-native/operator/pool-expansion.md new file mode 100644 index 00000000..482c99b5 --- /dev/null +++ b/content/installation/cloud-native/operator/pool-expansion.md @@ -0,0 +1,55 @@ +--- +title: Pool Expansion +description: Add storage capacity to a RustFS Tenant by appending a storage pool. +--- + +All pools in a Tenant form one RustFS cluster. Add capacity by appending a new pool to `spec.pools`; do not change the shape of an existing pool. + +:::warning[Existing pools are immutable] + +Do not change `servers` or `persistence.volumesPerServer` on an existing pool. The Operator creates an immutable StatefulSet for each pool. + +::: + +## 1. Check the Tenant + +```bash +kubectl -n storage-a get tenant tenant-a +kubectl -n storage-a get pods,pvc -l rustfs.tenant=tenant-a +``` + +Confirm that the Tenant is `Ready` and the cluster has enough compute and storage capacity. + +## 2. Add a pool + +Append the following entry to the existing `spec.pools` list in `tenant.yaml`. Keep all existing entries unchanged. + +```yaml title="tenant.yaml" +- name: pool-1 + servers: 2 + persistence: + volumesPerServer: 2 + volumeClaimTemplate: + storageClassName: standard + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Gi +``` + +This pool creates four PVCs: two servers multiplied by two volumes per server. Apply the complete Tenant manifest: + +```bash +kubectl apply -f tenant.yaml +``` + +## 3. Watch the expansion + +```bash +kubectl -n storage-a get tenant tenant-a -w +kubectl -n storage-a get pods,pvc \ + -l rustfs.pool=pool-1 +``` + +Wait for the Tenant to return to `Ready` before making another topology change. Increasing an existing PVC size is a separate Kubernetes storage operation and depends on the StorageClass. \ No newline at end of file diff --git a/content/installation/cloud-native/operator/tenant.md b/content/installation/cloud-native/operator/tenant.md new file mode 100644 index 00000000..08bec042 --- /dev/null +++ b/content/installation/cloud-native/operator/tenant.md @@ -0,0 +1,74 @@ +--- +title: Multi-Tenant +description: Create isolated RustFS Tenants and access their S3 API and Console services. +--- + +A `Tenant` represents one independent RustFS cluster. Use a separate namespace, credentials Secret, and Tenant resource for each team or workload. + +## 1. Create a namespace and credentials + +Create the Secret directly so credentials are not stored in a manifest: + +```bash +kubectl create namespace storage-a +kubectl -n storage-a create secret generic rustfs-tenant-creds \ + --from-literal=accesskey='' \ + --from-literal=secretkey='' +``` + +## 2. Define the Tenant + +This development example creates one RustFS Pod and one `10Gi` PVC. Replace `standard` with a StorageClass in your cluster. + +```yaml title="tenant.yaml" +apiVersion: rustfs.com/v1alpha1 +kind: Tenant +metadata: + name: tenant-a + namespace: storage-a +spec: + image: rustfs/rustfs:1.0.0-beta.10 + credsSecret: + name: rustfs-tenant-creds + pools: + - name: pool-0 + servers: 1 + persistence: + volumesPerServer: 1 + volumeClaimTemplate: + storageClassName: standard + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +``` + +## 3. Apply and verify + +```bash +kubectl apply -f tenant.yaml +kubectl -n storage-a get tenant,pods,pvc,svc +kubectl -n storage-a describe tenant tenant-a +``` + +## 4. Access RustFS + +```bash +kubectl -n storage-a port-forward svc/tenant-a-io 9000:9000 +kubectl -n storage-a port-forward svc/tenant-a-console 9001:9001 +``` + +Run the commands in separate terminals. Use `http://localhost:9000` as the S3 endpoint and open `http://localhost:9001` for the Tenant Console. + +To add another Tenant, repeat the process with a different namespace, Secret, and Tenant name. List all managed Tenants with: + +```bash +kubectl get tenants --all-namespaces +``` + +:::warning[Production topology] + +The one-server example is for evaluation. Production Tenants need a distributed pool layout, resource requests, scheduling constraints, and an immutable image reference. + +::: \ No newline at end of file diff --git a/content/installation/console-first-steps.md b/content/installation/console-first-steps.md deleted file mode 100644 index 4221a41d..00000000 --- a/content/installation/console-first-steps.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: "Console: First Steps" -description: "Sign in to the RustFS Console for the first time, create a bucket, and upload your first object." ---- - -Once your RustFS server is running, the embedded web console is the fastest way to verify the installation and start working with your data. This short tutorial walks you through your first session. - -## 1. Open the Console - -The console listens on port `9001` by default. In your browser, open: - -```text -http://:9001 -``` - -For a local test install, that is `http://127.0.0.1:9001`. - -## 2. Sign In - -Sign in with the credentials configured at install time: - -- If you used the [Linux quick-start script](./linux/quick-start.md), the access key and secret key were printed at the end of the installation output. -- If you started RustFS via Docker or a systemd unit, use the values of `RUSTFS_ACCESS_KEY` and `RUSTFS_SECRET_KEY` from your configuration (see the [Docker guide](./docker/index.md)). - -> For production deployments, change any default credentials before exposing the console. See the [Security Checklist](./checklists/security-checklists.md). - -## 3. A Quick Tour - -After signing in you land on the console home page, which shows: - -- **Buckets** — a list of your buckets, with a **Create Bucket** button in the top left. -- **Usage overview** — object count and storage consumption at a glance. -- **Access Keys** — management of programmatic credentials (see step 6). - -## 4. Create Your First Bucket - -1. On the Buckets page, in the top right corner, select **Create Bucket**. -2. Enter a bucket name (for example `my-first-bucket`) and click **Create**. - -![bucket creation](../management/bucket/images/bucket-creation-by-ui.png) - -Full details, including `mc` and API alternatives, are in [Bucket Creation](../management/bucket/creation.md). - -## 5. Upload Your First Object - -1. Click the bucket you just created. -2. In the top right corner, select **Upload File/Folder**. -3. Choose one or more local files and click **Start Upload**. - -![object creation from ui](../management/object/images/upload_file_from_ui.png) - -Click the uploaded object to view its details — size, ETag, content type, and a shareable link. - -![object details info](../management/object/images/object_details_info.png) - -More options are covered in [Object Creation](../management/object/creation.md). - -## 6. Create Access Keys for Applications - -The console sign-in credentials are administrator credentials — applications should use their own keys instead. Go to **Access Keys** in the console to create a scoped access key / secret key pair for your S3 clients and SDKs. See [Access Key Management](../administration/iam/access-token.md) for the full walkthrough. - -## Next Steps - -- Point any S3-compatible client at `http://:9000` (the S3 API port) with your new access keys. -- Explore [IAM management](../administration/iam/index.md) to add users and policies. -- Review the [production checklists](./checklists/index.md) before going live. diff --git a/content/installation/container/docker.md b/content/installation/container/docker.md new file mode 100644 index 00000000..26124785 --- /dev/null +++ b/content/installation/container/docker.md @@ -0,0 +1,61 @@ +--- +title: "Docker" +description: "Run a single-node RustFS instance with Docker and persistent container storage." +--- + +Use the official RustFS image to start a single-node instance with persistent storage. You need a working Docker Engine and permission to run containers. + +## 1. Pull the image + +```bash +docker pull rustfs/rustfs:latest +``` + +## 2. Create persistent storage + +Create a named volume so object data remains available when you replace the container: + +```bash +docker volume create rustfs-data +``` + +## 3. Start RustFS + +Replace the credential placeholders before running the container: + +```bash +docker run -d \ + --name rustfs \ + --restart unless-stopped \ + -p 9000:9000 \ + -p 9001:9001 \ + -v rustfs-data:/data \ + -e RUSTFS_ACCESS_KEY="" \ + -e RUSTFS_SECRET_KEY="" \ + -e RUSTFS_CONSOLE_ENABLE=true \ + rustfs/rustfs:latest \ + /data +``` + +:::warning[Set credentials when creating the container] + +Set unique `RUSTFS_ACCESS_KEY` and `RUSTFS_SECRET_KEY` environment variables before exposing RustFS to a network. Do not use the well-known `rustfsadmin` value for either credential. If the container was started without custom credentials, stop and recreate it with both `-e` options shown above; the `rustfs-data` volume remains intact. + +::: + +## 4. Verify the deployment + +Check the container and the S3 API health endpoint: + +```bash +docker ps --filter name=rustfs +curl --fail http://localhost:9000/health +``` + +The S3 API is available at `http://localhost:9000`, and the Console is available at `http://localhost:9001`. + +## Next steps + +- [RustFS Console](/administration/console) +- [Configure an S3 client](../../developer/examples/aws-cli.md) +- [TLS configuration](../../integration/tls-configured.md) \ No newline at end of file diff --git a/content/installation/docker/index.mdx b/content/installation/container/index.mdx similarity index 93% rename from content/installation/docker/index.mdx rename to content/installation/container/index.mdx index e6e32759..b7528a27 100644 --- a/content/installation/docker/index.mdx +++ b/content/installation/container/index.mdx @@ -14,6 +14,7 @@ This article is based on RustFS official Linux binary packages, packaging RustFS * Docker installed (≥ 20.10) and able to pull images and run containers normally * Local path `/mnt/rustfs/data` (or custom path) for mounting object data + * [`rc`](/operations/rc) installed on the administration host for the verification workflow 2. **Network and Firewall** * Ensure host ports 9000 (S3 API) and 9001 (Console) are open to external access (or consistent with custom ports) @@ -225,6 +226,12 @@ e07121ecdd39 rustfs/rustfs:latest "/entrypoint.sh Whether you start only the `rustfs-server` or together with observability services, the S3 API is served at `http://localhost:9000`, and the RustFS Console is at `http://localhost:9001` — open it in a browser and log in with the access key and secret key you configured above (the `` / `` placeholders). Generate a strong secret with, for example, `openssl rand -base64 24`, and never ship the placeholder values to production. +:::warning[Set container credentials immediately] + +Set unique `RUSTFS_ACCESS_KEY` and `RUSTFS_SECRET_KEY` environment variables before exposing RustFS to a network. Do not use the well-known `rustfsadmin` value for either credential. For `docker run`, pass both values with `-e`. For Docker Compose, define both variables in the `rustfs` service environment or in the environment file used for variable substitution, then recreate the service with `docker compose up -d rustfs`. + +::: + ## 4. Verification and Access 1. **View Container Status and Logs:** @@ -237,13 +244,13 @@ Whether you start only the `rustfs-server` or together with observability servic 2. **Test S3 API:** - Use `mc` or other S3 clients: + Use `rc` to verify the S3 API: ```bash # Use a unique access key and a strong, random secret (e.g. openssl rand -base64 24) - mc alias set rustfs http://localhost:9000 "" "" - mc mb rustfs/mybucket - mc ls rustfs + rc alias set rustfs http://localhost:9000 + rc bucket create rustfs/my-bucket + rc bucket list rustfs/ ``` If buckets can be successfully created and listed, deployment is effective. diff --git a/content/installation/container/meta.json b/content/installation/container/meta.json new file mode 100644 index 00000000..be4e8d9a --- /dev/null +++ b/content/installation/container/meta.json @@ -0,0 +1,7 @@ +{ + "title": "Container", + "pages": [ + "docker", + "podman" + ] +} \ No newline at end of file diff --git a/content/installation/container/podman.md b/content/installation/container/podman.md new file mode 100644 index 00000000..fdcb320a --- /dev/null +++ b/content/installation/container/podman.md @@ -0,0 +1,60 @@ +--- +title: "Podman" +description: "Run a single-node RustFS instance with Podman and persistent container storage." +--- + +Podman can run the official RustFS Open Container Initiative (OCI) image without a daemon. You need a working Podman installation and permission to create containers. + +## 1. Pull the image + +```bash +podman pull docker.io/rustfs/rustfs:latest +``` + +## 2. Create persistent storage + +Create a named volume so object data remains available when you replace the container: + +```bash +podman volume create rustfs-data +``` + +## 3. Start RustFS + +Replace the credential placeholders before running the container: + +```bash +podman run -d \ + --name rustfs \ + -p 9000:9000 \ + -p 9001:9001 \ + -v rustfs-data:/data \ + -e RUSTFS_ACCESS_KEY="" \ + -e RUSTFS_SECRET_KEY="" \ + -e RUSTFS_CONSOLE_ENABLE=true \ + docker.io/rustfs/rustfs:latest \ + /data +``` + +:::warning[Set credentials when creating the container] + +Set unique `RUSTFS_ACCESS_KEY` and `RUSTFS_SECRET_KEY` environment variables before exposing RustFS to a network. Do not use the well-known `rustfsadmin` value for either credential. If the container was started without custom credentials, stop and recreate it with both `-e` options shown above; the `rustfs-data` volume remains intact. + +::: + +## 4. Verify the deployment + +Check the container and the S3 API health endpoint: + +```bash +podman ps --filter name=rustfs +curl --fail http://localhost:9000/health +``` + +The S3 API is available at `http://localhost:9000`, and the Console is available at `http://localhost:9001`. + +## Next steps + +- [RustFS Console](/administration/console) +- [Configure an S3 client](../../developer/examples/aws-cli.md) +- [TLS configuration](../../integration/tls-configured.md) \ No newline at end of file diff --git a/content/installation/index.md b/content/installation/index.md index 815916eb..e7985e53 100644 --- a/content/installation/index.md +++ b/content/installation/index.md @@ -1,17 +1,26 @@ --- -title: "RustFS Installation Guide" +title: "Installation" description: "Choose the right RustFS deployment path: local trial, single-node or multi-node production, containers, and Kubernetes." --- -RustFS is a distributed object storage system written in Rust, fully compatible with the S3 protocol and released under the Apache 2.0 license. It runs on Linux, Windows, macOS, FreeBSD, and containers, across x86, ARM, RISC-V, and other CPU architectures. To learn more about the project itself, see [What is RustFS?](../concepts/introduction.md). +RustFS is a distributed object storage system written in Rust and released under the Apache 2.0 license. It runs on Linux, Windows, macOS, FreeBSD, and containers, across x86, ARM, RISC-V, and other CPU architectures. + +:::warning[Change the default credentials immediately] + +After installation, set unique values for `RUSTFS_ACCESS_KEY` and `RUSTFS_SECRET_KEY` before exposing RustFS to a network. Do not use the well-known `rustfsadmin` value for either credential. + +- For a Linux binary or systemd installation, set both values in `/etc/default/rustfs`, then restart the `rustfs` service. +- For Docker, Podman, or Docker Compose, pass both values as container environment variables when creating the container. Recreate an existing container if it was started without them. + +::: ## Choose Your Path | Your goal | Recommended path | Guide | | - | - | - | -| Try RustFS on a local machine | One-command install script, or a container | [Linux Quick Start](./linux/quick-start.md) · [Docker](./docker/index.md) | +| Try RustFS on a local machine | One-command install script, or a container | [Linux Quick Start](./linux/quick-start.md) · [Container](./container/index.mdx) | | Single-server production | SNSD (one disk) or SNMD (multiple disks) | [Installing RustFS on Linux](./linux/index.md) | -| Multi-server production cluster | MNMD, after completing the production checklists | [Multiple Node Multiple Disk](./linux/multiple-node-multiple-disk.md) · [Checklists](./checklists/index.md) | +| Multi-server production cluster | MNMD, after completing the production checklists | [Multiple Node Multiple Disk](./linux/multiple-node-multiple-disk.md) · [Checklists](./requirement/checklists/index.md) | | Kubernetes / cloud-native | Container orchestration deployment | [Cloud Native](./cloud-native/index.md) | | Windows or macOS host | Native installation | [Windows](./windows/index.md) · [macOS](./macos/index.md) | @@ -23,6 +32,6 @@ RustFS is a distributed object storage system written in Rust, fully compatible | [SNMD](./linux/single-node-multiple-disk.md) | 1 | Multiple | Up to M parity disks within the node | Medium, non-critical business on a single server | | [MNMD](./linux/multiple-node-multiple-disk.md) | 4+ | Multiple per node | Disk- and node-level via erasure coding across servers | Production workloads | -## Production Checklists +## Checklist -Before any production deployment, work through the [Pre-Installation Checklists](./checklists/index.md) — hardware, network, software, and security — to make sure your environment meets production guidance. +Before any production deployment, work through the [Pre-Installation Checklists](./requirement/checklists/index.md) — hardware, network, software, and security — to make sure your environment meets production guidance. diff --git a/content/installation/linux/index.md b/content/installation/linux/index.md index 505105d5..8f9f4f1b 100644 --- a/content/installation/linux/index.md +++ b/content/installation/linux/index.md @@ -5,6 +5,12 @@ description: "Choose a Linux deployment mode — SNSD, SNMD, or MNMD — and ins This section covers installing RustFS on Linux servers. For a one-command trial installation, use the [Quick Start](./quick-start.md). For a manual installation, pick one of the three deployment modes below — all three share the same [prerequisites and service setup](./prerequisites-and-service.md), and differ only in topology and volume configuration. +:::warning[Change the default credentials immediately] + +After installation, set unique `RUSTFS_ACCESS_KEY` and `RUSTFS_SECRET_KEY` values in `/etc/default/rustfs`. Do not use the well-known `rustfsadmin` value for either credential. Restart the service with `sudo systemctl restart rustfs` after changing the file. + +::: + ## Single Node Single Disk (SNSD) One server, one data disk. The simplest mode, with no redundancy — a disk failure means data loss, so rely on backups. Suitable for development, testing, and low-density non-critical business. @@ -25,4 +31,4 @@ Four or more servers, each with one or more disks. Erasure coding spans servers, ## Before Production -Work through the [Pre-Installation Checklists](../checklists/index.md) — hardware, network, software, and security — before deploying to production. If you don't need production standards, you can skip them. +Work through the [Pre-Installation Checklists](../requirement/checklists/index.md) — hardware, network, software, and security — before deploying to production. If you don't need production standards, you can skip them. diff --git a/content/installation/linux/multiple-node-multiple-disk.md b/content/installation/linux/multiple-node-multiple-disk.md index 30ec6981..eaf7bfb3 100644 --- a/content/installation/linux/multiple-node-multiple-disk.md +++ b/content/installation/linux/multiple-node-multiple-disk.md @@ -29,7 +29,7 @@ flowchart TD class N1D1,N1D2,N1D3,N1D4,N2D1,N2D2,N2D3,N2D4,N3D1,N3D2,N3D3,N3D4,N4D1,N4D2,N4D3,N4D4 disk ``` -Before installation, review the [Pre-Installation Checklists](../checklists/index.md) and ensure all items meet production guidance. +Before installation, review the [Pre-Installation Checklists](../requirement/checklists/index.md) and ensure all items meet production guidance. ## Hostnames @@ -117,6 +117,6 @@ tail -f /var/logs/rustfs/rustfs*.log ## Next Steps -- Put a load balancer in front of the cluster — see the [Nginx integration guide](../../integration/nginx.md). +- Put a load balancer in front of the cluster — see the [Nginx integration guide](/developer/integration/reverse-proxy/nginx). - Enable TLS for production traffic — see [TLS configuration](../../integration/tls-configured.md). -- Review [Availability and Resiliency](../../upgrade-scale/availability-and-resiliency.md) before scaling. +- Review [Pool Expansion](../../operations/scaling/storage-pool-expansion.md) before scaling. diff --git a/content/installation/linux/prerequisites-and-service.md b/content/installation/linux/prerequisites-and-service.md index 72bc7702..0714609f 100644 --- a/content/installation/linux/prerequisites-and-service.md +++ b/content/installation/linux/prerequisites-and-service.md @@ -50,7 +50,7 @@ timedatectl status If the status is "synchronized", time synchronization is working properly. -## Capacity and EC Planning +## Capacity Planning When planning object storage capacity, we recommend considering: @@ -59,13 +59,7 @@ When planning object storage capacity, we recommend considering: - Planning cycle: How long should this hardware planning last? (recommended: 3 years) - Your company's hardware iteration and update cycles. -EC (Erasure Coding) planning is as follows: - -| Scenario | Recommended Parity Level | Description | -| - | - | - | -| Standard Production Environment | EC:4 | Can tolerate up to 4 disk (or node) failures, achieving a good balance between reliability and storage efficiency. | -| High Availability Requirements | EC:4 - 8 or higher | Suitable for scenarios with extremely high data availability requirements, but sacrifices more storage space. | -| Development Test Environment | EC:2 | Provides basic redundancy protection, suitable for non-critical business. | +Review [EC Configuration](../requirement/ec-configuration.md) to calculate usable capacity, understand the automatic parity defaults, and validate any explicit parity or erasure-set width before deployment. ## Disk Planning diff --git a/content/installation/linux/quick-start.md b/content/installation/linux/quick-start.md index 6d8efc73..f68a6678 100644 --- a/content/installation/linux/quick-start.md +++ b/content/installation/linux/quick-start.md @@ -10,6 +10,7 @@ This guide takes you from an empty Linux server to a working RustFS instance: in - A Linux server (x86_64 or aarch64) with `systemd`, and root or sudo access - `unzip` installed, and outbound network access to download the package - Ports `9000` (S3 API) and `9001` (Console) reachable from your machine +- [`rc`](/operations/rc) installed on your workstation for the command-line verification workflow ## 1. Install and start RustFS @@ -61,18 +62,18 @@ Open `http://:9001` in your browser and sign in with the access key a 2. Open the bucket and use the upload action to add any local file. 3. Click the uploaded object to view its details — you have a working object store. -Prefer the command line? The same two operations with the [MinIO Client (`mc`)](../../developer/mc.md): +Prefer the command line? Configure [`rc`](/operations/rc), then perform the same operations: ```bash -mc alias set rustfs http://:9000 -mc mb rustfs/my-bucket -mc cp ./hello.txt rustfs/my-bucket -mc ls rustfs/my-bucket +rc alias set rustfs http://:9000 +rc bucket create rustfs/my-bucket +rc object copy /path/to/hello.txt rustfs/my-bucket/hello.txt +rc object list rustfs/my-bucket ``` ```text -Bucket created successfully `rustfs/my-bucket`. -[2026-07-15 10:00:00 UTC] 12B hello.txt +✓ Bucket 'rustfs/my-bucket' created successfully. +/path/to/hello.txt -> rustfs/my-bucket/hello.txt ``` @@ -84,6 +85,6 @@ The quick install runs RustFS in **Single Node Single Disk (SNSD)** mode — zer - **Plan a production deployment** — choose a topology, then follow its guide: - [Single Node Single Disk (SNSD)](./single-node-single-disk.md) — dev and small workloads - [Single Node Multiple Disk (SNMD)](./single-node-multiple-disk.md) — disk-level fault tolerance on one machine - - [Multiple Node Multiple Disk (MNMD)](./multiple-node-multiple-disk.md) — production-grade availability and scale, with the [pre-installation checklists](../checklists/index.md) -- **Prefer containers?** — [Install with Docker](../docker/index.md) + - [Multiple Node Multiple Disk (MNMD)](./multiple-node-multiple-disk.md) — production-grade availability and scale, with the [pre-installation checklists](../requirement/checklists/index.md) +- **Prefer containers?** — [Install with a container](../container/index.mdx) - **Connect your application** — [SDKs and examples](../../developer/sdk/index.md) diff --git a/content/installation/linux/single-node-multiple-disk.md b/content/installation/linux/single-node-multiple-disk.md index b634a14b..95dd8f00 100644 --- a/content/installation/linux/single-node-multiple-disk.md +++ b/content/installation/linux/single-node-multiple-disk.md @@ -26,7 +26,7 @@ flowchart TD - 1 server with multiple data disks (this example uses 4 disks mounted at `/data/rustfs0` through `/data/rustfs3`). - Erasure coding spreads data and parity shards across the disks; fault tolerance is limited to disk failures within the single node. - Format each disk with XFS and mount it separately (e.g., labels `RUSTFS0` – `RUSTFS3`), as described in the prerequisites page. -- For production deployments, also review the [Pre-Installation Checklists](../checklists/index.md). +- For production deployments, also review the [Pre-Installation Checklists](../requirement/checklists/index.md). ## Prerequisites and Service Setup diff --git a/content/installation/linux/single-node-single-disk.md b/content/installation/linux/single-node-single-disk.md index 9eb18d79..cf912d2f 100644 --- a/content/installation/linux/single-node-single-disk.md +++ b/content/installation/linux/single-node-single-disk.md @@ -25,7 +25,7 @@ flowchart TD - 1 server, 1 data disk (e.g., an XFS-formatted disk mounted at `/data/rustfs0`). - No erasure coding across disks — fault tolerance depends entirely on backups. -- For production deployments, also review the [Pre-Installation Checklists](../checklists/index.md). +- For production deployments, also review the [Pre-Installation Checklists](../requirement/checklists/index.md). ## Prerequisites and Service Setup diff --git a/content/installation/macos/index.md b/content/installation/macos/index.md index 044d10e9..2cb290a7 100644 --- a/content/installation/macos/index.md +++ b/content/installation/macos/index.md @@ -1,59 +1,249 @@ --- -title: "Installing RustFS on macOS" -description: "Quick ways to start RustFS on macOS, using the graphical one-click startup package." +title: "Install RustFS on macOS" +description: "Install a local RustFS instance on macOS with RustFS Launcher or the standalone server binary." --- -On macOS, you can use three methods for installation: +This guide provides two ways to run **RustFS** on macOS: use the graphical **RustFS Launcher**, or run the standalone server binary from Terminal. Both methods create a local, single-node instance suitable for evaluation, development, and testing. -1. Docker -2. Graphical one-click startup package -3. Binary package +:::note[Deployment scope] -> This article mainly explains how to use the RustFS **graphical one-click startup package** to quickly launch RustFS. +These procedures run RustFS as a desktop process, not as a macOS background service. For a production or distributed deployment, use the [Linux installation guides](/installation/linux). -## 1. Preparation +::: + +## 1. Check your Mac architecture + +RustFS distinguishes between Apple Silicon (`arm64`) and Intel (`x86_64`) Macs. Open Terminal and run: + +```bash +uname -m +``` + +Use the result to choose the correct package: -:::note +| Result | Mac type | Current standalone binary availability | +| --- | --- | --- | +| `arm64` | Apple Silicon | Download the macOS AArch64 package from current RustFS releases. | +| `x86_64` | Intel | RustFS releases after `v1.0.0-beta.9` do not provide a prebuilt Intel macOS package. Build the current version from source. | -**Graphical startup mode** only supports single-node single-disk mode, more suitable for development, debugging, and testing environments. +:::warning[Intel Mac release boundary] + +Do not run the Apple Silicon binary on an Intel Mac. If you need a RustFS version newer than `v1.0.0-beta.9` on Intel macOS, follow the source-build procedure in this guide. An older beta.9 binary does not contain fixes and features from later releases. ::: -1. For detailed introduction about startup modes, please refer to [Installation Modes](../linux/quick-start.md#mode) -2. Download the installation package, modify permissions, and start. +Before continuing, also make sure that: + +- You have permission to install or run applications and write to the selected data directory. +- TCP ports `9000` and `9001` are available, or you have selected two other unused ports. +- You have created a dedicated data directory, such as `$HOME/rustfs/data`. + +```bash +mkdir -p "$HOME/rustfs/data" +``` + +:::warning[Protect the data directory] + +Use an empty directory on a disk with enough free space. Do not store unrelated files in this directory, and do not edit RustFS data files while the server is running. + +::: + +## 2. Choose an installation method + +| Method | Use it when | Architecture note | +| --- | --- | --- | +| RustFS Launcher | You want a graphical setup and built-in log viewer. | Download a Launcher package that matches Apple Silicon or Intel. | +| Standalone binary | You want explicit Terminal commands or scripting. | Current releases provide Apple Silicon binaries; Intel users build releases after beta.9 from source. | + +## Install with RustFS Launcher + +### 3. Download and install Launcher + +1. Open the [RustFS Launcher Releases page](https://github.com/rustfs/launcher/releases). +2. Select the latest release and expand **Assets**. +3. Download the macOS installer that matches your architecture: AArch64 for Apple Silicon or x86-64 for Intel. Do not download **Source code** unless you intend to build Launcher yourself. +4. Open the downloaded disk image and move **RustFS Launcher** to **Applications**. +5. Open **RustFS Launcher** from **Applications**. + +If macOS blocks the first launch, confirm that the package came from the official `rustfs/launcher` Releases page. Then open **System Settings > Privacy & Security** and allow that specific application. Do not disable Gatekeeper globally. + +### 4. Configure Launcher + +Configure the fields in the Launcher window: + +| Section | Field | Recommended value | Description | +| --- | --- | --- | --- | +| Dataset Mount | Data Path | `$HOME/rustfs/data` | Existing directory where RustFS stores object data. | +| Ports & Access | API Port | `9000` | S3-compatible API port. | +| Ports & Access | Host | `127.0.0.1` | Restricts access to this Mac. | +| Ports & Access | Console Endpoint | Enabled | Enables the web Console; Launcher disables it by default. | +| Ports & Access | Console Port | `9001` | Console port, which must differ from the API port. | +| Credentials | Access Key | `` | Administrator access key for this instance. | +| Credentials | Secret Key | `` | Administrator secret key for this instance. | + +Use **Browse** to select the data directory or drag it into Launcher. Launcher requires the directory to exist before startup. The macOS file picker displays the expanded path instead of the literal `$HOME` variable. + +Replace the credential placeholders with your own values. Launcher initially displays default local-test credentials; do not retain them on a Mac accessible by other users. + +:::warning[Network exposure] + +Keep **Host** set to `127.0.0.1` unless another computer must connect to RustFS. A non-loopback address exposes the API and Console on the corresponding network interfaces. Configure the macOS firewall and strong credentials before allowing remote access. + +::: + +### 5. Start and verify RustFS + +1. Select **Launch RustFS**. +2. Wait for Launcher to report that the configured endpoint is online. +3. Open `http://localhost:9001` and sign in with the access key and secret key that you configured. +4. Use `http://localhost:9000` as the endpoint for S3-compatible clients. + +![RustFS Launcher showing the service online with API port 9000, Console port 9001, and successful startup logs](../windows/images/windows-rustfs-launcher.jpg) + +If you changed the host or ports, use those values instead. The Console URL is available only when **Console Endpoint** is enabled. + +Launcher locks its configuration while it manages a running process. It also creates a `logs` directory next to the data directory. For example, selecting `$HOME/rustfs/data` places server log files under `$HOME/rustfs/logs`. + +### 6. Stop or exit Launcher + +Select **Stop RustFS** before changing configuration, disconnecting an external data disk, or shutting down macOS. + +Closing the Launcher window hides it in the menu bar; it does not stop RustFS. To stop the managed process and exit Launcher, open the Launcher menu bar icon and select **Quit**. Select **Show**, or select the icon, to restore the window. + +## Install the standalone binary + +### 7. Download the Apple Silicon binary -## 2. Download +Use this procedure when `uname -m` returns `arm64`: -Download the latest release from [GitHub Releases](https://github.com/rustfs/rustfs/releases) or the [official download page](https://rustfs.com/download/). +1. Open the [RustFS Releases page](https://github.com/rustfs/rustfs/releases). +2. Select the required release and download the macOS AArch64 ZIP archive from **Assets**. The asset name begins with `rustfs-macos-aarch64`. +3. Create a program directory and extract the archive. Replace `` with the version in the downloaded filename: -## 3. Modify Permissions +```bash +mkdir -p "$HOME/rustfs/bin" +unzip "$HOME/Downloads/rustfs-macos-aarch64-.zip" -d "$HOME/rustfs/bin" +chmod +x "$HOME/rustfs/bin/rustfs" +"$HOME/rustfs/bin/rustfs" --help +``` -Please confirm that this program has relevant execution permissions in the macOS operating system. +If the archive contains a platform-specific binary name instead of `rustfs`, use that filename in the remaining commands or rename it to `$HOME/rustfs/bin/rustfs`. -## 4. Start the Service +### 8. Build the Intel binary from source -1. Double-click the startup icon -2. Click configure disk -3. Click "Start Service", and RustFS service starts successfully. +Use this procedure when `uname -m` returns `x86_64` and you need a release newer than `v1.0.0-beta.9`. -![macos startup](./images/macos-setup.jpg) +1. Install the Xcode command-line tools: -## 5. Modify Configuration +```bash +xcode-select --install +``` -Click the modify button (gear-shaped button) in the upper right corner to modify: +2. Install the current stable Rust toolchain from [rustup.rs](https://rustup.rs/), then open a new Terminal session. +3. Clone RustFS and check out the release that you need: -1. Server default port -2. Default administrator username and password -3. Specified disk directory +```bash +git clone https://github.com/rustfs/rustfs.git +cd rustfs +git checkout +``` -![RustFS macOS configuration](./images/setting.jpg) +4. Build the Intel macOS target with the repository build script: -## 6. Access Console +```bash +./build-rustfs.sh --platform x86_64-apple-darwin +``` -After successful startup, visit `http://127.0.0.1:7001` to access the console. +The build script includes Console assets by default and verifies the resulting binary. Use the binary reported at the end of the build output in the next step. -:::note +:::note[Source-build dependencies] -Port `7001` applies to the macOS desktop launcher. If you run the standalone `rustfs` server binary instead, the console listens on port `9001` by default. +Building from source requires the tools used by the selected RustFS release in addition to Rust and the Xcode command-line tools. If the build reports a missing dependency, follow the requirements in that release's repository README rather than substituting a binary built for Apple Silicon. ::: + +### 9. Start the standalone server + +Set credentials for the current Terminal session and assign the path to your downloaded or compiled binary: + +```bash +export RUSTFS_ACCESS_KEY="" +export RUSTFS_SECRET_KEY="" +export RUSTFS_BIN="$HOME/rustfs/bin/rustfs" + +"$RUSTFS_BIN" server \ + --address "127.0.0.1:9000" \ + --console-enable true \ + --console-address "127.0.0.1:9001" \ + "$HOME/rustfs/data" +``` + +For an Intel source build, replace `RUSTFS_BIN` with the binary path printed by `build-rustfs.sh`. Keep the Terminal window open while RustFS is running. Press `Control+C` to stop the server. + +:::tip[Credential handling] + +Session environment variables avoid placing credentials directly in the command line. For repeated or automated use, prefer the `RUSTFS_ACCESS_KEY_FILE` and `RUSTFS_SECRET_KEY_FILE` environment variables and protect those files with macOS permissions. + +::: + +### 10. Verify the standalone server + +Open `http://localhost:9001` and sign in with the configured credentials. Use `http://localhost:9000` as the endpoint for S3-compatible clients. RustFS defaults to path-style addressing, so configure clients accordingly. + +To perform an end-to-end check, create a bucket named `my-bucket`, upload `hello.txt`, and confirm that the object appears in the bucket. + +## Confirm a successful installation + +Whether you used Launcher, an Apple Silicon binary, or an Intel source build, a successful Console sign-in opens the **Buckets** page. + +![RustFS Console Buckets page after a successful sign-in](../windows/images/rustfs-console-login.png) + +## Troubleshooting + +### macOS blocks the application or binary + +Verify that the file came from the official RustFS GitHub organization. For Launcher, allow the specific application under **System Settings > Privacy & Security**. For a standalone binary that macOS has quarantined, inspect the warning and verify the download source before changing its security attributes. + +### The binary architecture does not match + +Run `uname -m` and inspect the binary: + +```bash +uname -m +file "$RUSTFS_BIN" +``` + +Use an AArch64 binary on Apple Silicon. On an Intel Mac, build releases after `v1.0.0-beta.9` from source for `x86_64-apple-darwin`. + +### The data path is rejected + +Confirm that the directory exists and that your macOS account has read and write access. Launcher does not create the selected data directory for you. + +### The API or Console port is unavailable + +The ports must be numbers from `1` to `65535`, must differ from each other, and must not already be in use. Inspect the default ports: + +```bash +lsof -nP -iTCP:9000 -iTCP:9001 -sTCP:LISTEN +``` + +Stop the conflicting application or select unused ports, then start RustFS again. + +### The Console does not open + +For Launcher, confirm that **Console Endpoint** is enabled. For the standalone binary, confirm that the process is still running and that `--console-enable true` is present. + +### Launcher reports an external RustFS process + +Another process is listening on the configured API address, but Launcher did not start it. Stop that process or choose another API port. Launcher cannot stop a process it does not manage. + +### RustFS exits after launch + +In Launcher, inspect both the application and RustFS tabs in the built-in log viewer. For a standalone installation, inspect the Terminal output. Common causes include a missing data directory, insufficient permissions, an architecture mismatch, or a port conflict. + +## Next steps + +- Review the [S3 API guide](/administration/protocols/s3) before connecting a client. +- Learn how to [create a bucket](/administration/data/bucket/creation). +- See [status checks](/operations/status-check) for additional verification methods. +- Use the [Linux installation guides](/installation/linux) for a production-oriented deployment. diff --git a/content/installation/meta.json b/content/installation/meta.json new file mode 100644 index 00000000..bbb7164e --- /dev/null +++ b/content/installation/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Installation", + "defaultOpen": true, + "pages": [ + "requirement", + "linux", + "container", + "cloud-native", + "[Windows](/installation/windows)", + "[macOS](/installation/macos)" + ] +} \ No newline at end of file diff --git a/content/installation/checklists/hardware-checklists.md b/content/installation/requirement/checklists/hardware-checklists.md similarity index 100% rename from content/installation/checklists/hardware-checklists.md rename to content/installation/requirement/checklists/hardware-checklists.md diff --git a/content/installation/checklists/hardware-selection.md b/content/installation/requirement/checklists/hardware-selection.md similarity index 100% rename from content/installation/checklists/hardware-selection.md rename to content/installation/requirement/checklists/hardware-selection.md diff --git a/content/installation/checklists/index.md b/content/installation/requirement/checklists/index.md similarity index 94% rename from content/installation/checklists/index.md rename to content/installation/requirement/checklists/index.md index 0a121882..7b1f73a8 100644 --- a/content/installation/checklists/index.md +++ b/content/installation/requirement/checklists/index.md @@ -1,5 +1,5 @@ --- -title: "Checklists" +title: "Checklist" description: "Pre-installation checklists." --- @@ -15,7 +15,7 @@ For professional support, contact us at hello@rustfs.com. For community support, submit an issue on GitHub. -## Checklists +## Checklist - [Hardware Checklist](./hardware-checklists.md) - [Hardware Selection](./hardware-selection.md) diff --git a/content/installation/requirement/checklists/meta.json b/content/installation/requirement/checklists/meta.json new file mode 100644 index 00000000..1fd22fd6 --- /dev/null +++ b/content/installation/requirement/checklists/meta.json @@ -0,0 +1,10 @@ +{ + "title": "Checklist", + "pages": [ + "[Hardware Checklist](/installation/requirement/checklists/hardware-checklists)", + "[Hardware Selection](/installation/requirement/checklists/hardware-selection)", + "[Network Checklist](/installation/requirement/checklists/network-checklists)", + "[Software Checklist](/installation/requirement/checklists/software-checklists)", + "[Security Checklist](/installation/requirement/checklists/security-checklists)" + ] +} diff --git a/content/installation/checklists/network-checklists.md b/content/installation/requirement/checklists/network-checklists.md similarity index 100% rename from content/installation/checklists/network-checklists.md rename to content/installation/requirement/checklists/network-checklists.md diff --git a/content/installation/checklists/security-checklists.md b/content/installation/requirement/checklists/security-checklists.md similarity index 100% rename from content/installation/checklists/security-checklists.md rename to content/installation/requirement/checklists/security-checklists.md diff --git a/content/installation/checklists/software-checklists.md b/content/installation/requirement/checklists/software-checklists.md similarity index 100% rename from content/installation/checklists/software-checklists.md rename to content/installation/requirement/checklists/software-checklists.md diff --git a/content/installation/requirement/disk-preparation.md b/content/installation/requirement/disk-preparation.md new file mode 100644 index 00000000..e8fed45d --- /dev/null +++ b/content/installation/requirement/disk-preparation.md @@ -0,0 +1,83 @@ +--- +title: "Disk Preparation" +description: "Identify, format, mount, and verify dedicated data disks before installing RustFS on Linux." +--- + +Prepare each RustFS data disk as an independent XFS file system with a stable label and mount point. Complete these steps on every Linux node before configuring `RUSTFS_VOLUMES`. + +:::warning[Formatting destroys data] + +The `mkfs.xfs` command erases the selected device. Confirm the device name, back up any required data, and ensure the disk is not used by the operating system before continuing. + +::: + +## 1. Identify the data disks + +List block devices, file systems, labels, and current mount points: + +```bash +sudo lsblk -o NAME,SIZE,TYPE,FSTYPE,LABEL,UUID,MOUNTPOINTS +``` + +Choose only dedicated data disks. Do not format the operating-system disk or a device that contains data you need to retain. + +## 2. Format a disk with XFS + +The following example prepares `/dev/sdb` and assigns the label `RUSTFS0`. Replace the device name for your environment and use a unique label for each disk. + +```bash +sudo mkfs.xfs -f -i size=512 -n ftype=1 -L RUSTFS0 /dev/sdb +``` + +Repeat the command for additional disks with labels such as `RUSTFS1`, `RUSTFS2`, and `RUSTFS3`. + +## 3. Create a persistent mount + +Create the mount point: + +```bash +sudo mkdir -p /data/rustfs0 +``` + +Add an entry to `/etc/fstab` so the disk is mounted by its stable file-system label after a restart: + +```ini title="/etc/fstab" +LABEL=RUSTFS0 /data/rustfs0 xfs defaults,noatime,nodiratime 0 0 +``` + +For more reliable device selection, we recommend mounting by file-system UUID. A UUID uniquely identifies the file system and avoids mounting the wrong disk if labels are duplicated or Linux device names change. Retrieve the UUID after formatting: + +```bash +sudo blkid -s UUID -o value /dev/sdb +``` + +Replace `` with the returned value, and use this entry instead of the `LABEL` entry above: + +```ini title="/etc/fstab" +UUID= /data/rustfs0 xfs defaults,noatime,nodiratime 0 0 +``` + +Mount all entries and confirm the result: + +```bash +sudo mount -a +findmnt /data/rustfs0 +``` + +For multiple disks, create one mount point and one `/etc/fstab` entry per file-system UUID. + +## 4. Verify the prepared disks + +Confirm that every expected mount uses XFS and has sufficient free space: + +```bash +df -hT /data/rustfs0 +sudo xfs_info /data/rustfs0 +``` + +Use the verified mount paths when you configure `RUSTFS_VOLUMES`. Keep disk labels and mount paths consistent across nodes in a distributed deployment. + +## Next steps + +- [Choose a deployment topology](/installation#deployment-mode-comparison) +- [Continue with Linux prerequisites and service setup](/installation/linux/prerequisites-and-service) \ No newline at end of file diff --git a/content/installation/requirement/ec-configuration.md b/content/installation/requirement/ec-configuration.md new file mode 100644 index 00000000..e38e4f7e --- /dev/null +++ b/content/installation/requirement/ec-configuration.md @@ -0,0 +1,115 @@ +--- +title: "EC Configuration" +description: "Plan and configure RustFS erasure-set size and parity before deploying storage pools." +--- + +RustFS uses Reed-Solomon erasure coding (EC) to distribute each object across the drives in an erasure set. Use this page to estimate usable capacity, understand the automatic defaults, and decide whether your deployment needs an explicit parity setting. + +## Understand the layout + +Each storage pool is divided into one or more erasure sets. For a set with `N` drives and `M` parity shards: + +- data shards: `N - M` +- parity shards: `M` +- maximum unavailable shards that can be reconstructed: `M` +- approximate capacity efficiency: `(N - M) / N` + +For example, a 16-drive set with `EC:4` stores 12 data shards and 4 parity shards. Its approximate capacity efficiency is 75% before file-system, metadata, and operational overhead. + +:::note[Failure domains] + +Parity describes unavailable shards within one erasure set. Node-level fault tolerance depends on how the set's drives are distributed across nodes. Do not assume that `EC:4` always tolerates four complete node failures; a node can host multiple drives from the same set. + +::: + +## Automatic parity + +We recommend leaving `RUSTFS_STORAGE_CLASS_STANDARD` unset unless you have a specific, validated durability or capacity requirement. RustFS derives STANDARD parity independently for each pool from its drives per erasure set: + +| Drives per erasure set (`N`) | Automatic STANDARD parity | Data shards | Approximate efficiency | +| --- | --- | --- | --- | +| 1 | `EC:0` | 1 | 100% | +| 2–3 | `EC:1` | `N - 1` | 50%–67% | +| 4–5 | `EC:2` | `N - 2` | 50%–60% | +| 6–7 | `EC:3` | `N - 3` | 50%–57% | +| 8–16 | `EC:4` | `N - 4` | 50%–75% | + +Multi-drive erasure sets contain between 2 and 16 drives. A single-drive deployment is a separate layout with zero parity. + +:::warning[Use the set width, not the cluster total] + +Validate parity against the number of drives in each erasure set, not the total number of drives in the cluster. A cluster can contain multiple sets or pools with different widths. + +::: + +## Estimate usable capacity + +For a pool whose sets all use the same width and parity, estimate EC capacity with: + +$$ +\text{usable capacity} \approx \text{raw capacity} \times \frac{N-M}{N} +$$ + +For example, 16 equal 10 TiB drives arranged as one 16-drive set with `EC:4` provide approximately: + +$$ +160\ \text{TiB} \times \frac{16-4}{16} = 120\ \text{TiB} +$$ + +Reserve additional capacity for XFS, object metadata, versioning, incomplete uploads, healing, and normal operational headroom. Treat the result as a planning estimate, not guaranteed available space. + +## Configure parity + +Set EC variables in the same environment file used by the RustFS service, such as `/etc/default/rustfs`. + +To use the recommended automatic STANDARD parity, omit `RUSTFS_STORAGE_CLASS_STANDARD` or leave it empty: + +```ini title="/etc/default/rustfs" +RUSTFS_STORAGE_CLASS_STANDARD= +``` + +To pin parity explicitly, use the `EC:` format: + +```ini title="/etc/default/rustfs" +RUSTFS_STORAGE_CLASS_STANDARD=EC:4 +RUSTFS_STORAGE_CLASS_RRS=EC:1 +``` + +`RUSTFS_STORAGE_CLASS_STANDARD` controls writes using the `STANDARD` storage class. `RUSTFS_STORAGE_CLASS_RRS` controls `REDUCED_REDUNDANCY` writes and defaults to one parity shard on multi-drive sets. RustFS accepts these two storage classes for local writes. + +Explicit settings must satisfy all of these rules: + +- parity must be no greater than half of the drives in every target set: `M <= N / 2` +- STANDARD parity must be greater than or equal to REDUCED_REDUNDANCY parity when both are non-zero +- the configured parity must be valid for every pool, including the narrowest pool + +RustFS validates explicit storage classes against each pool during startup. An invalid value causes startup to fail instead of silently reducing parity. Remove the explicit STANDARD value to return to per-pool automatic parity, or choose a value every pool can satisfy. + +## Control erasure-set width + +RustFS normally selects a valid set width automatically from the volume topology. Multi-drive set widths range from 2 through 16. + +`RUSTFS_ERASURE_SET_DRIVE_COUNT` can pin the width when you need a specific symmetric layout: + +```ini title="/etc/default/rustfs" +RUSTFS_ERASURE_SET_DRIVE_COUNT=16 +``` + +The value must divide the pool topology symmetrically and be valid for every volume expansion pattern. RustFS rejects a width that cannot evenly partition the configured endpoints. Leave this variable unset unless you have verified the complete pool layout. + +## Validate the configuration + +Before starting or restarting RustFS: + +1. Determine the drives per erasure set for every pool. +2. Confirm that explicit parity does not exceed half of the narrowest set width. +3. Estimate usable capacity and reserve operational headroom. +4. Keep the same service environment configuration on every node. + +After startup, use the Console **Status** page or `rc admin info cluster` to confirm the backend layout, erasure-coding parity, drive availability, and pool membership. See [Status Check](/operations/status-check) for the complete workflow. + +## Next steps + +- [Prepare data disks](./disk-preparation.md) +- [Choose a deployment topology](/installation#deployment-mode-comparison) +- [Review all storage environment variables](/reference/environment-variables#storage--erasure) \ No newline at end of file diff --git a/content/installation/requirement/meta.json b/content/installation/requirement/meta.json new file mode 100644 index 00000000..6538db30 --- /dev/null +++ b/content/installation/requirement/meta.json @@ -0,0 +1,9 @@ +{ + "title": "Requirement", + "pages": [ + "[Topology Selection](/installation#deployment-mode-comparison)", + "ec-configuration", + "disk-preparation", + "checklists" + ] +} \ No newline at end of file diff --git a/content/installation/windows/images/rustfs-console-login.png b/content/installation/windows/images/rustfs-console-login.png new file mode 100644 index 00000000..d5c7342f Binary files /dev/null and b/content/installation/windows/images/rustfs-console-login.png differ diff --git a/content/installation/windows/images/windows-rustfs-launcher.jpg b/content/installation/windows/images/windows-rustfs-launcher.jpg new file mode 100644 index 00000000..60b0f827 Binary files /dev/null and b/content/installation/windows/images/windows-rustfs-launcher.jpg differ diff --git a/content/installation/windows/index.md b/content/installation/windows/index.md index 20c21adc..f95b47e6 100644 --- a/content/installation/windows/index.md +++ b/content/installation/windows/index.md @@ -1,56 +1,191 @@ --- -title: "Installing RustFS on Windows" -description: "Use Windows one-click startup for RustFS." +title: "Install RustFS on Windows" +description: "Install a local RustFS instance on Windows with RustFS Launcher or the standalone server binary." --- -## 1. Preparation +This guide provides two ways to run **RustFS** on Windows: use the graphical **RustFS Launcher**, or download and run the standalone server binary from PowerShell. Both methods create a local, single-node instance suitable for evaluation, development, and testing. -:::note +:::note[Deployment scope] -Windows startup **mode** only supports single-node single-disk mode, more suitable for development, debugging, and testing environments. +These procedures run RustFS as a desktop process, not as a Windows service. For a production or distributed deployment, use the [Linux installation guides](/installation/linux). ::: -1. For detailed introduction about startup modes, please refer to [Installation Modes](../linux/quick-start.md#mode); +## 1. Check the prerequisites -2. Download the installation package, modify permissions, and start. +Before you begin, make sure that: -## 2. Download +- You are using 64-bit Windows on an x86-64 processor. Launcher currently bundles the Windows x86-64 RustFS binary. +- You have permission to install or run applications and to write to the selected data directory. +- TCP ports `9000` and `9001` are available, or you have selected two other unused ports. +- You have created a dedicated data directory, such as `D:\rustfs\data`. -Download the latest release from [GitHub Releases](https://github.com/rustfs/rustfs/releases) or the [official download page](https://rustfs.com/download/). +:::warning[Protect the data directory] -## 3. Modify Permissions +Use an empty directory on a disk with enough free space. Do not store unrelated files in this directory, and do not edit RustFS data files while the server is running. -Please confirm that this program has relevant execution permissions in the Windows operating system. +::: + +## 2. Choose an installation method + +| Method | Use it when | Management model | +| --- | --- | --- | +| RustFS Launcher | You want a graphical setup and built-in log viewer. | Launcher starts and stops the bundled RustFS process. | +| Standalone binary | You want explicit PowerShell commands or scripting. | The PowerShell session owns the RustFS process. | + +## Install with RustFS Launcher + +### 3. Download and install Launcher + +1. Open the [RustFS Launcher Releases page](https://github.com/rustfs/launcher/releases). +2. Select the latest release and download its Windows x86-64 installer from **Assets**. Do not download **Source code**, which is for building Launcher from source. +3. Double-click the installer and follow the prompts. +4. Open **RustFS Launcher** from the Start menu if it does not open automatically. + +If Microsoft Defender SmartScreen displays a warning, verify that the installer came from the official `rustfs/launcher` Releases page before allowing it to run. Do not disable Windows security features globally. + +### 4. Configure Launcher + +Configure the fields in the Launcher window: + +| Section | Field | Recommended value | Description | +| --- | --- | --- | --- | +| Dataset Mount | Data Path | `D:\rustfs\data` | Existing directory where RustFS stores object data. | +| Ports & Access | API Port | `9000` | S3-compatible API port. | +| Ports & Access | Host | `127.0.0.1` | Restricts access to this computer. | +| Ports & Access | Console Endpoint | Enabled | Enables the web Console; Launcher disables it by default. | +| Ports & Access | Console Port | `9001` | Console port, which must differ from the API port. | +| Credentials | Access Key | `` | Administrator access key for this instance. | +| Credentials | Secret Key | `` | Administrator secret key for this instance. | + +Select the data directory with **Browse** or drag it into Launcher. Launcher requires the directory to exist before startup. + +Replace the credential placeholders with your own values. Launcher initially displays default local-test credentials; do not retain them on a computer accessible by other users. + +:::warning[Network exposure] + +Keep **Host** set to `127.0.0.1` unless another computer must connect to RustFS. A non-loopback address exposes the API and Console on the corresponding network interfaces. Configure Windows Defender Firewall and strong credentials before allowing remote access. + +::: + +### 5. Start and verify RustFS + +1. Select **Launch RustFS**. +2. Wait for Launcher to report that the configured endpoint is online. +3. Open `http://localhost:9001` and sign in with the access key and secret key that you configured. +4. Use `http://localhost:9000` as the endpoint for S3-compatible clients. + +![RustFS Launcher showing the service online with API port 9000, Console port 9001, and successful startup logs](./images/windows-rustfs-launcher.jpg) + +If you changed the host or ports, use those values instead. The Console URL is available only when **Console Endpoint** is enabled. + +Launcher locks its configuration while it manages a running process. It also creates a `logs` directory next to the data directory. For example, a data path of `D:\rustfs\data` places server log files under `D:\rustfs\logs`. + +### 6. Stop or exit Launcher + +Select **Stop RustFS** before changing configuration, disconnecting the data disk, or shutting down Windows. + +Closing the Launcher window hides it in the Windows system tray; it does not stop RustFS. To stop the managed process and exit Launcher, right-click the tray icon and select **Quit**. Select **Show**, or left-click the tray icon, to restore the window. -## Double-click the Startup Icon +## Install the standalone binary -1. Double-click the startup icon; +### 7. Download and extract the binary -2. Click configure disk; +1. Open the [RustFS Releases page](https://github.com/rustfs/rustfs/releases). +2. Select the required release and download the Windows x86-64 ZIP archive from **Assets**. The asset name begins with `rustfs-windows-x86_64`. +3. Open PowerShell and create directories for the program and its data: -3. Click "Start Service", and RustFS service starts successfully. +```powershell +New-Item -ItemType Directory -Force -Path C:\rustfs\bin +New-Item -ItemType Directory -Force -Path D:\rustfs\data +``` -![windows startup](./images/windows-setup.jpg) +4. Extract the downloaded archive. Replace `` with the version in the downloaded filename: -## 4. Modify Configuration +```powershell +Expand-Archive -Path "$HOME\Downloads\rustfs-windows-x86_64-.zip" -DestinationPath C:\rustfs\bin -Force +``` -Click the modify button (gear-shaped button) in the upper right corner to modify: +5. Confirm that the extracted binary runs: -1. Server default port; +```powershell +Set-Location C:\rustfs\bin +.\rustfs.exe --help +``` -2. Default administrator username and password; +If the archive extracts `rustfs.exe` into a nested directory, change to that directory or move the executable into `C:\rustfs\bin` before continuing. -3. Specified disk directory; +### 8. Start the standalone server -![RustFS windows configuration](./images/setting.jpg) +Set credentials only for the current PowerShell session, then start RustFS with explicit addresses: -## 5. Access Console +```powershell +$env:RUSTFS_ACCESS_KEY = "" +$env:RUSTFS_SECRET_KEY = "" -After successful startup, visit `http://127.0.0.1:7001` to access the console. +Set-Location C:\rustfs\bin +.\rustfs.exe server ` + --address "127.0.0.1:9000" ` + --console-enable true ` + --console-address "127.0.0.1:9001" ` + "D:\rustfs\data" +``` -:::note +Keep the PowerShell window open while RustFS is running. Press `Ctrl+C` in that window to stop the server. -Port `7001` applies to the Windows desktop launcher. If you run the standalone `rustfs` server binary instead, the console listens on port `9001` by default. +:::tip[Credential handling] + +Session environment variables avoid placing credentials directly in the command line. For repeated or automated use, prefer the `RUSTFS_ACCESS_KEY_FILE` and `RUSTFS_SECRET_KEY_FILE` environment variables and protect those files with Windows access controls. ::: + +### 9. Verify the standalone server + +Open `http://localhost:9001` and sign in with the configured credentials. Use `http://localhost:9000` as the endpoint for S3-compatible clients. RustFS defaults to path-style addressing, so configure clients accordingly. + +To perform an end-to-end check, create a bucket named `my-bucket`, upload `hello.txt`, and confirm that the object appears in the bucket. + +## Confirm a successful installation + +Whether you used Launcher or the standalone binary, a successful Console sign-in opens the **Buckets** page. From here, you can create `my-bucket` and begin storing objects. + +![RustFS Console Buckets page after a successful sign-in](./images/rustfs-console-login.png) + +## Troubleshooting + +### Windows blocks the installer or executable + +Confirm that the file came from the official RustFS GitHub organization. Inspect the file's **Properties** dialog and, when Windows provides the option, select **Unblock** only after verifying the source. + +### The data path is rejected + +Confirm that the directory exists and that your Windows account has read and write access. Launcher does not create the selected data directory for you. + +### The API or Console port is unavailable + +The ports must be numbers from `1` to `65535`, must differ from each other, and must not already be in use. Inspect the default ports in PowerShell: + +```powershell +Get-NetTCPConnection -LocalPort 9000,9001 -ErrorAction SilentlyContinue +``` + +Stop the conflicting application or select unused ports, then start RustFS again. + +### The Console does not open + +For Launcher, confirm that **Console Endpoint** is enabled. For the standalone binary, confirm that the process is still running and that `--console-enable true` is present. The current Console default is port `9001`; port `7001` is not used by the current Launcher configuration. + +### Launcher reports an external RustFS process + +Another process is listening on the configured API address, but Launcher did not start it. Stop that process or choose another API port. Launcher cannot stop a process it does not manage. + +### RustFS exits after launch + +In Launcher, inspect both the application and RustFS tabs in the built-in log viewer. For a standalone installation, inspect the PowerShell output. Common causes include a missing data directory, insufficient permissions, or a port conflict. + +## Next steps + +- Review the [S3 API guide](/administration/protocols/s3) before connecting a client. +- Learn how to [create a bucket](/administration/data/bucket/creation). +- See [status checks](/operations/status-check) for additional verification methods. +- Use the [Linux installation guides](/installation/linux) for a production-oriented deployment. diff --git a/content/integration/nginx.md b/content/integration/nginx.md deleted file mode 100644 index ca1df934..00000000 --- a/content/integration/nginx.md +++ /dev/null @@ -1,252 +0,0 @@ ---- -title: "Nginx Reverse Proxy" -description: "Configuration for implementing Nginx reverse proxy with RustFS." ---- - -# Nginx Integration - -Nginx integration enables: - -- Log collection. -- Load balancing. -- URL forwarding. -- URL blocking. - -## Prerequisites - -- RustFS Server is installed and running. -- RustFS port is known. -- Nginx is installed. -- RustFS IP addresses are known. - -## Configuration - -```nginx title="/etc/nginx/conf.d/rustfs.conf" -upstream rustfs { - least_conn; - server 127.0.0.1:9000; -} - -upstream rustfs-console { - least_conn; - server 127.0.0.1:9001; -} - - -map $http_upgrade $proxy_set_header_connection { - # If the Upgrade request header is present, also send `Connection: upgrade` upstream; - # otherwise send a blank Connection header instead of the default `Connection: close`, - # preserving upstream keepalives. - default "upgrade"; - "" ""; -} - -server { - listen 80; - listen [::]:80; - server_name YOUR_DOMAIN; - - # Allow special characters in headers - ignore_invalid_headers off; - # Allow any size file to be uploaded. - # Set to a value such as 1000m; to restrict file size to a specific value - client_max_body_size 0; - # Disable buffering - proxy_buffering off; - proxy_request_buffering off; - - location / { - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # Disable Nginx from converting HEAD to GET - proxy_cache_convert_head off; - - proxy_connect_timeout 300; - # Default is HTTP/1, keepalive is only enabled in HTTP/1.1 - proxy_http_version 1.1; - chunked_transfer_encoding off; - - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $proxy_set_header_connection; - - - - - proxy_pass http://rustfs; # This uses the upstream directive definition to load balance - } -} - - -server { - listen 8080; - listen [::]:8080; - server_name YOUR_DOMAIN; - - # Allow special characters in headers - ignore_invalid_headers off; - # Allow any size file to be uploaded. - # Set to a value such as 1000m; to restrict file size to a specific value - client_max_body_size 0; - # Disable buffering - proxy_buffering off; - proxy_request_buffering off; - - location / { - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # Disable Nginx from converting HEAD to GET - proxy_cache_convert_head off; - - proxy_connect_timeout 300; - # Default is HTTP/1, keepalive is only enabled in HTTP/1.1 - proxy_http_version 1.1; - chunked_transfer_encoding off; - - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $proxy_set_header_connection; - - - - - proxy_pass http://rustfs-console; # This uses the upstream directive definition to load balance - } -} - - -``` - -#### Important Notice - -:::warning[Key configuration items] - -The `proxy_cache_convert_head off` directive **must be added** to the Nginx configuration for the following reasons: - -- By default, Nginx converts HEAD requests to GET requests for caching. -- This conversion will cause S3 V4 signature verification to fail. -- The symptom is `Bucket not found` or `403 Access Denied` error when accessing the storage bucket. - -Refer to [Nginx Official Documentation](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_convert_head). - -::: - -## Multi-Machine Load Balancing - -If you need to add multiple RustFS servers in a distributed environment, please adjust DNS resolution or local Hosts addresses in advance, and modify and add servers. - -```nginx title="/etc/nginx/conf.d/rustfs.conf (multi-node upstream)" -upstream rustfs { - least_conn; - server 10.0.0.1:9000; - server 10.0.0.2:9000; - server 10.0.0.3:9000; - server 10.0.0.4:9000; -} - - -upstream rustfs-console { - least_conn; - server 10.0.0.1:9001; - server 10.0.0.2:9001; - server 10.0.0.3:9001; - server 10.0.0.4:9001; -} -``` - - -## Dedicated DNS Mode -Create or configure dedicated DNS names for the RustFS service — one hostname for the S3 API and one for the Console. - -:::warning[Do not proxy the S3 API under a path prefix] - -S3 clients sign the request path (AWS Signature V4). If you expose the API under a prefix such as `/api/`, RustFS receives paths like `/api//...`, interprets `api` as a bucket name, and signature validation fails. Always serve the S3 API from the root of its own hostname. - -::: - -For example: -S3 endpoint: `s3.rustfs.dev` -Console: `console.rustfs.dev` - -~~~nginx title="/etc/nginx/conf.d/rustfs.conf (dedicated DNS)" -map $http_upgrade $proxy_set_header_connection { - # If the Upgrade request header is present, also send `Connection: upgrade` upstream; - # otherwise send a blank Connection header instead of the default `Connection: close`, - # preserving upstream keepalives. - default "upgrade"; - "" ""; -} - -# S3 API -server { - listen 443 ssl; - listen [::]:443 ssl; - http2 on; - server_name s3.rustfs.dev; - - ssl_certificate /etc/nginx/certs/rustfs.dev.pem; - ssl_certificate_key /etc/nginx/certs/rustfs.dev.key; - - # Allow special characters in headers - ignore_invalid_headers off; - # Allow any size file to be uploaded. - # Set to a value such as 1000m; to restrict file size to a specific value - client_max_body_size 0; - # Disable buffering - proxy_buffering off; - proxy_request_buffering off; - - location / { - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - proxy_connect_timeout 300; - # Default is HTTP/1, keepalive is only enabled in HTTP/1.1 - proxy_http_version 1.1; - chunked_transfer_encoding off; - - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $proxy_set_header_connection; - - proxy_pass http://127.0.0.1:9000; - } -} - -# Console -server { - listen 443 ssl; - listen [::]:443 ssl; - http2 on; - server_name console.rustfs.dev; - - ssl_certificate /etc/nginx/certs/rustfs.dev.pem; - ssl_certificate_key /etc/nginx/certs/rustfs.dev.key; - - ignore_invalid_headers off; - client_max_body_size 0; - proxy_buffering off; - proxy_request_buffering off; - - location / { - proxy_set_header Host $http_host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - proxy_connect_timeout 300; - # Default is HTTP/1, keepalive is only enabled in HTTP/1.1 - proxy_http_version 1.1; - chunked_transfer_encoding off; - - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $proxy_set_header_connection; - proxy_pass http://127.0.0.1:9001; - } -} -~~~ diff --git a/content/management/bucket/index.md b/content/management/bucket/index.md deleted file mode 100644 index 0e21f346..00000000 --- a/content/management/bucket/index.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: "RustFS Bucket Management" -description: "Buckets are the fundamental containers for data in RustFS. This section covers bucket creation, deletion, and management." ---- - -Buckets organize and manage data. Each bucket has a unique name and can contain multiple objects, providing logical grouping for easier access and management. - -- [Bucket Creation](./creation.md) -- [Bucket Deletion](./deletion.md) -- Bucket Copying - diff --git a/content/management/bucket/meta.json b/content/management/bucket/meta.json deleted file mode 100644 index 2f9e3b72..00000000 --- a/content/management/bucket/meta.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "title": "Bucket Management", - "pages": [ - "[Bucket Creation](/management/bucket/creation)", - "[Bucket Deletion](/management/bucket/deletion)" - ] -} diff --git a/content/management/index.md b/content/management/index.md deleted file mode 100644 index 4565e162..00000000 --- a/content/management/index.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "Management Guide" -description: "Guide to managing RustFS buckets, objects, and permissions." ---- - -This section covers: - -- [Bucket Management](./bucket/index.md) -- [Object Management](./object/index.md) -- [IAM Management](../administration/iam/index.md) \ No newline at end of file diff --git a/content/management/object/index.md b/content/management/object/index.md deleted file mode 100644 index d3dbacd5..00000000 --- a/content/management/object/index.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: "RustFS Object Management" -description: "Guide to RustFS object management, including creation, deletion, versioning, locking, and scanning." ---- - -Objects are the fundamental storage units in RustFS, consisting of data, metadata, and a unique key. This section covers: - -- [Object Upload](./creation.md) -- [Object Deletion](./deletion.md) -- Object Versioning -- Object Locking -- Object Sharing -- [Object Scanning](./scanner.md) diff --git a/content/management/object/meta.json b/content/management/object/meta.json deleted file mode 100644 index 3e501e72..00000000 --- a/content/management/object/meta.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "title": "Object Management", - "pages": [ - "[Object Upload](/management/object/creation)", - "[Object Deletion](/management/object/deletion)" - ] -} diff --git a/content/meta.json b/content/meta.json index ee1b30ab..f7ff8975 100644 --- a/content/meta.json +++ b/content/meta.json @@ -1,62 +1,12 @@ { "root": true, "pages": [ - "---Getting Started---", - "[What is RustFS?](/concepts/introduction)", - "[Quick Start](/installation/linux/quick-start)", - "[Console First Steps](/installation/console-first-steps)", - - "---Core Concepts---", - "[Design Architecture](/concepts/architecture)", - "[Erasure Coding](/concepts/principle/erasure-coding)", - "[Performance Comparison](/concepts/comparison)", - - "---Installation---", - "[Installation Guide](/installation)", - "installation/checklists", - "installation/linux", - "[Docker](/installation/docker)", - "[Kubernetes (Helm)](/installation/cloud-native)", - "[Windows](/installation/windows)", - "[macOS](/installation/macos)", - - "---Administration---", - "[Management Guide](/management)", - "management/bucket", - "management/object", - "administration/iam", - "[Nginx Reverse Proxy](/integration/nginx)", - "[TLS Configuration](/integration/tls-configured)", - "[Virtual-Host Access](/integration/virtual)", - - "---Operations---", - "[Scaling & Availability](/upgrade-scale)", - "[Monitoring & Alerting](/operations/monitoring)", - "[Rolling Upgrades](/upgrade-scale/upgrade)", - "[Adding a Storage Pool](/upgrade-scale/availability-and-resiliency)", - "[Pool Decommission & Rebalance](/operations/decommission)", - "[Credential Management](/operations/credentials)", - "[Object Scanner](/management/object/scanner)", - "[Troubleshooting](/troubleshooting)", - "[Cold Start & Quorum Loss](/operations/cold-start)", - "[Disk Failures](/troubleshooting/driver)", - "[Node Failures](/troubleshooting/node)", - "[Data Healing](/troubleshooting/healing)", - - "---Developer---", - "[Developer Guide](/developer)", - "developer/sdk", - "developer/examples", - "[MinIO Client (mc)](/developer/mc)", - "[MCP Server](/developer/mcp)", - "[Open Source License](/developer/license)", - - "---Reference---", - "[Environment Variables](/reference/environment-variables)", - "[CLI](/reference/cli)", - "[Ports & Health Endpoints](/reference/ports)", - "[Metrics](/reference/metrics)", - "[Usage Limits](/concepts/limit)", - "[Glossary](/concepts/glossary)" + "installation", + "administration", + "security-compliance", + "operations", + "troubleshooting", + "developer", + "reference" ] } diff --git a/content/operations/cold-start.md b/content/operations/cold-start.md deleted file mode 100644 index f5099e9b..00000000 --- a/content/operations/cold-start.md +++ /dev/null @@ -1,88 +0,0 @@ ---- -title: "Cold Start and Quorum Loss" -description: "This article explains how RustFS nodes behave when they start before the cluster has quorum, how to read the degraded-mode signals, and how to diagnose the most common startup failures." ---- - -## Degraded Startup in One Paragraph - -When several nodes of an erasure-coded cluster went down together (power loss, host maintenance, a crash-looping rollout) and come back one at a time, the early nodes cannot reach the storage read quorum. They do **not** exit. The process stays alive in **degraded mode**, answers S3 requests with `503`, and recovers **automatically** as soon as enough peers are online. Do not restart-loop degraded nodes — just keep starting the remaining ones. - -## Why One Node Cannot Serve Alone - -Erasure coding shards every object — including internal metadata such as IAM users and policies under `.rustfs.sys` — across the drives of a set. Reading anything back requires a read quorum of shards. With a set's drives spread over several nodes, a single node can never satisfy that quorum by itself; the cluster becomes readable once roughly half the nodes of a set are up (internal configuration objects are written with maximum parity). Distributed locking similarly needs a majority of nodes' lock RPC endpoints. - -## The 503 Contract - -While a node is not yet ready, S3 requests receive: - -- status `503 Service Unavailable`; -- header `Retry-After: 5` — clients should retry, the condition is temporary; -- header `x-rustfs-readiness-pending` naming the blocking dependency: - - `storage_quorum` — waiting for enough nodes/disks for the erasure read quorum; - - `iam` — storage is up, the IAM cache is still loading; - - `startup_finalization` — the last startup steps are being published. - -Probe the readiness endpoint for per-dependency detail: - -```bash -curl -s http://:9000/health/ready | jq -``` - -A degraded response includes a `details` object (`storage` / `iam` / `lock`, plus `kms` when configured) and a `degradedReasons` array. Verified reason values: - -| `degradedReasons` value | Meaning | -| --- | --- | -| `storage_quorum_unavailable` | Erasure read quorum not met | -| `iam_not_ready` | IAM cache still loading | -| `lock_quorum_unavailable` | Lock RPC majority not reachable (cluster probes only) | -| `kms_not_ready` | Configured KMS not reachable | -| `peer_health_unavailable` | Peer health check failing | -| `cluster_health_timeout` | Cluster health evaluation timed out | -| `storage_and_iam_unavailable`, `storage_and_lock_unavailable`, `iam_and_lock_unavailable`, `storage_iam_and_lock_unavailable` | Combined variants of the above | - -`GET /health` and `GET /health/live` stay `200` throughout — the process is alive, only readiness is pending. Point Kubernetes liveness probes at `/health` and readiness probes at `/health/ready`, or a degraded-but-recovering node will be killed mid-recovery. - -## Why You Should Not Restart-Loop - -1. **Recovery is automatic.** As soon as enough peers are online, pending nodes finish IAM bootstrap on the next retry and flip `/health/ready` to `200` on their own. Restarting does not speed this up — it throws away retry progress. -2. **Logs tell you what is missing.** The IAM recovery loop logs `event="iam_bootstrap_retry_failed"` with an actionable `hint` field (for example, "storage read quorum not met yet; waiting for enough cluster nodes/disks to come online"). After repeated failures the level escalates from WARN to ERROR — this still does not kill the process. -3. **A node process exiting during startup is a bug, not the design.** The fatal IAM/lock startup path was removed after v1.0.0-beta.5; upgrade if you still see nodes exit while waiting for quorum. - -## Tuning - -- `RUSTFS_STARTUP_READINESS_MAX_WAIT_SECS` (default `120`): how long startup waits for full readiness before continuing in degraded mode with background recovery. Raising it delays the listener during genuinely slow starts; lowering it surfaces degraded mode sooner. Recovery retries continue regardless of this limit. - -## Recommended Cold-Start Procedure - -1. Start all nodes (order does not matter). Early nodes sit in degraded mode. -2. Watch readiness converge: - - ```bash - for n in node1 node2 node3 node4; do - echo -n "$n: "; curl -s http://$n:9000/health/ready | jq -r '.status' - done - ``` - -3. Once every node reports `ok`, the cluster is fully serving. If a node is still degraded **after** all peers are up, check network reachability between nodes (peer RPC ports), compare per-node clocks, then read `degradedReasons` and the `hint` field of the IAM retry logs. - -## Common Startup Failures - -These fail fast at container start (checks performed by the image entrypoint) or during listener setup, before any degraded-mode logic applies: - -| Symptom | Cause | Fix | -| --- | --- | --- | -| `ERROR: RUSTFS_ACCESS_KEY must not be empty.` | Credential env var set to an empty string (often an unexpanded compose interpolation like `${VAR}`) | Set a real value or remove the variable | -| `ERROR: Set either RUSTFS_ACCESS_KEY or RUSTFS_ACCESS_KEY_FILE, not both.` | Both direct and file-based credential sources configured | Keep exactly one source | -| `ERROR: RUSTFS_ACCESS_KEY_FILE points to an unreadable file.` | Secret file missing or wrong permissions | Mount the secret and check file modes | -| `WARNING: ... uses the default rustfsadmin credential.` | Running with default credentials | Not fatal, but set real credentials before exposing the listener — see [Credential Management](./credentials) | -| Startup aborts with `VolumeNotFound` | The local path component of a distributed volume URL does not exist on disk — RustFS does not auto-create disk roots | Create the data directories (the container entrypoint creates local paths listed in `RUSTFS_VOLUMES`, but verify host mounts) | -| `HTTP listener bind failed` in logs | Port 9000 (or 9001 for the console) already in use, or address unavailable | Free the port or change `RUSTFS_ADDRESS` / `RUSTFS_CONSOLE_ADDRESS` | - -:::note -Keep node clocks synchronized (NTP/chrony). S3 request signing is time-sensitive, and the rolling-restart runbook explicitly lists per-node clock drift as a cause of nodes staying degraded after the cluster is otherwise back. -::: - -## Related - -- Rolling restarts without downtime (restart one node at a time, wait for `/health/ready` = `200` before the next) follow the same readiness signals — see the availability documentation in [Availability and Resiliency](../upgrade-scale/availability-and-resiliency). -- [Monitoring and Alerting](./monitoring) shows how to alert on `rustfs_runtime_readiness_ready` so a stuck-degraded node pages you. diff --git a/content/operations/credentials.md b/content/operations/credentials.md index 54c72732..f10b5a0b 100644 --- a/content/operations/credentials.md +++ b/content/operations/credentials.md @@ -15,7 +15,9 @@ RUSTFS_SECRET_KEY= If neither is provided, the server falls back to the built-in default `rustfsadmin` / `rustfsadmin`. :::warning + The default `rustfsadmin` credentials are public and well-known. The container entrypoint prints a warning but still starts. Always set non-default credentials before exposing ports 9000/9001 beyond localhost. Additionally, a multi-node cluster running with an all-default pair **must** set `RUSTFS_RPC_SECRET` (see below) — the server refuses to derive internode RPC auth from the default secret key. + ::: ## File-Based Injection (Docker/Kubernetes Secrets) @@ -56,7 +58,9 @@ secrets: All nodes of a cluster must run with the **same** root credential pair — internode RPC authentication is derived from it unless `RUSTFS_RPC_SECRET` is set explicitly. :::note + The step-by-step procedure below is standard operational practice composed from verified building blocks (env/file configuration plus the verified rolling-restart readiness signals); RustFS does not currently document an online root-credential rotation API. + ::: 1. If you have not already, set an explicit `RUSTFS_RPC_SECRET` (same value on all nodes) **before** the rotation. This decouples internode auth from the credential pair, so the cluster tolerates nodes temporarily running with mixed old/new root credentials during the rolling restart. @@ -98,7 +102,9 @@ For drop-in migration, RustFS accepts credential variables under legacy and MinI The `MINIO_` prefix mapping covers a broader allowlist of variables (address, console address, audit webhook settings, and more), applied at startup as "external-prefix compatibility mappings". :::note + Aliases are deprecated. When one is used, the server logs a one-time warning of the form `Environment variable MINIO_ROOT_USER is deprecated, use RUSTFS_ROOT_USER instead`. Use the `RUSTFS_`-prefixed canonical names in new deployments; the canonical name always wins when both are set. + ::: ## Beyond the Root Credential diff --git a/content/operations/decommission.md b/content/operations/decommission.md deleted file mode 100644 index 8a7c529c..00000000 --- a/content/operations/decommission.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -title: "Pool Decommission and Rebalance" -description: "This article describes how to retire a server pool with the decommission workflow and how to spread existing data onto new pools with rebalance, including the admin API endpoints, progress monitoring, and abort semantics." ---- - -## Concepts - -A RustFS cluster grows by adding **server pools** — additional groups of nodes/drives listed in `RUSTFS_VOLUMES` (space-separated expansion expressions). Two data-movement operations manage pools over their lifecycle: - -- **Decommission** drains all objects off a pool onto the remaining active pools, so the pool can be removed from the deployment. Movement is one-directional and the pool is retired afterwards. -- **Rebalance** redistributes existing objects across **all** pools after an expansion, so a newly added (empty) pool takes its fair share. No pool is removed. - -The two are mutually exclusive: rebalance refuses to start while a decommission is in progress, and both require a multi-pool deployment (single-pool clusters reject either operation because there is nowhere to move data). - -## When to Decommission - -- Retiring old hardware after adding a replacement pool. -- Shrinking a cluster that was previously expanded. -- Consolidating small pools into a larger one. - -## Prerequisites - -1. **At least one active pool must remain.** You cannot decommission every pool; the request is rejected if no active pool would be left. -2. **Remaining pools need capacity.** The server verifies before starting that the free space on the remaining active pools is at least the used bytes of the pool(s) being drained **plus a 30% overhead**. Otherwise the start request fails with `insufficient target pool capacity`. -3. **Completed pools cannot be re-decommissioned.** Completion means the pool should now be removed from the deployment configuration (`RUSTFS_VOLUMES` / Helm pool list). Failed or canceled pools may be retried. -4. Healthy cluster: run decommission with all nodes up; the operation persists its state and resumes after restarts, but starting it on a degraded cluster adds risk. - -:::warning -Decommission moves data. Take a fresh backup or verify your replication targets before draining a pool, and schedule it in a low-traffic window. -::: - -## Admin API Endpoints - -The admin API is served on port 9000 under the `/rustfs/admin/v3` prefix (a MinIO-compatible `/minio/admin` prefix also exists). All requests must be signed (AWS Signature V4) with credentials that hold the decommission admin permission — the root credential works. The `pool` query parameter takes the pool's command-line expression exactly as configured, or a zero-based pool index with `by-id=true`. - -| Method | Path | Purpose | -| --- | --- | --- | -| `GET` | `/rustfs/admin/v3/pools/list` | List pools and their status | -| `GET` | `/rustfs/admin/v3/pools/status?pool=` | Status of one pool | -| `GET` | `/rustfs/admin/v3/decommission/status[?pool=]` | Decommission progress (all pools or one) | -| `POST` | `/rustfs/admin/v3/pools/decommission?pool=` | Start draining a pool (comma-separated multi-pool targets are queued) | -| `POST` | `/rustfs/admin/v3/pools/cancel?pool=` | Cancel a running decommission | -| `POST` | `/rustfs/admin/v3/pools/clear?pool=` | Clear failed/canceled decommission metadata | - -Example with `curl` (SigV4 signing via `--aws-sigv4`): - -```bash -# Start decommissioning pool 0 (by index) -curl -X POST \ - --aws-sigv4 "aws:amz:us-east-1:s3" \ - --user ":" \ - "http://:9000/rustfs/admin/v3/pools/decommission?pool=0&by-id=true" - -# Or address the pool by its volumes expression -curl -X POST \ - --aws-sigv4 "aws:amz:us-east-1:s3" \ - --user ":" \ - "http://:9000/rustfs/admin/v3/pools/decommission?pool=http://server{1...4}/disk{1...4}" -``` - -The request can be sent to any node; if the target pool's leader is a different node, RustFS forwards the operation over the authenticated internode RPC channel. - -The upstream Helm chart documents the same workflow through an admin CLI as `rc admin pool ls` / `rc admin decommission` / `rc admin rebalance start `. - -:::note -The `rc` admin CLI is referenced by the upstream Helm README but may not be generally available in your distribution yet. The HTTP admin API above and the RustFS console are the verified interfaces; treat `rc admin ...` commands as equivalent shorthand where the tool is available. -::: - -## Monitoring Progress - -Poll the decommission status endpoint: - -```bash -curl -s \ - --aws-sigv4 "aws:amz:us-east-1:s3" \ - --user ":" \ - "http://:9000/rustfs/admin/v3/decommission/status" | jq -``` - -Each pool entry reports a `decommissionInfo` object with (field names as serialized): - -- `startTime`, `startSize`, `totalSize`, `currentSize` -- `complete`, `failed`, `canceled` -- `objectsDecommissioned`, `objectsDecommissionedFailed` -- `bytesDecommissioned`, `bytesDecommissionedFailed` - -The operation is finished when `complete` is `true` and the failed counters are zero. Only one pool actively moves data at a time; additional targets in a multi-pool request wait in a queue. - -After completion, remove the drained pool from `RUSTFS_VOLUMES` (or the Helm `pools` list — entries there are append-only, so decommission **before** removing an entry) and restart the cluster with the new topology. - -## Cancel, Clear, and Rollback Semantics - -- **Cancel** (`POST /rustfs/admin/v3/pools/cancel?pool=`) stops the running drain. The pool is marked `canceled` and stops receiving decommission traffic; a canceled (or failed) pool can be decommissioned again later. -- **Clear** (`POST /rustfs/admin/v3/pools/clear?pool=`) removes failed/canceled decommission metadata only, so status output is clean again. -- **There is no data rollback.** Objects already moved to other pools stay where they are; cancel/clear never move data back. This is by design — a partially drained pool is still fully functional, just emptier. - -## Rebalance After Expansion - -After adding a pool, existing objects stay where they were written; only new writes prefer the pool with more free space. To actively spread existing data: - -| Method | Path | Purpose | -| --- | --- | --- | -| `POST` | `/rustfs/admin/v3/rebalance/start` | Start a cluster-wide rebalance (no query parameters; returns `{"id": ...}`) | -| `GET` | `/rustfs/admin/v3/rebalance/status` | Per-pool progress: `objects`, `versions`, `bytes`, `remainingBuckets`, current `bucket`/`object`, `elapsed`, `eta` | -| `POST` | `/rustfs/admin/v3/rebalance/stop` | Stop the rebalance | - -```bash -curl -X POST \ - --aws-sigv4 "aws:amz:us-east-1:s3" \ - --user ":" \ - "http://:9000/rustfs/admin/v3/rebalance/start" -``` - -Rules enforced by the server: - -- rejected on single-pool deployments; -- rejected while a decommission is in progress (`cannot start rebalance while decommission is in progress`); -- rejected when a rebalance is already running (`rebalance is already in progress`). - -Rebalance runs until pools converge toward equal usage ratios; you can stop it at any time — like decommission, stopping never undoes moves already made. - -## Local Test Rig - -The upstream repository ships `docker-compose.decommission.yml`, a single-container two-pool layout useful for rehearsing the workflow before touching production: - -```bash -# Two pools inside one container: /data/pool0/disk{1...4} and /data/pool1/disk{1...4} -# S3 on host port 9100, console on 9101 -docker compose -f docker-compose.decommission.yml up -d -``` diff --git a/content/operations/event-notifications.md b/content/operations/event-notifications.md new file mode 100644 index 00000000..a6162a4f --- /dev/null +++ b/content/operations/event-notifications.md @@ -0,0 +1,205 @@ +--- +title: "Event Notifications" +description: "Configure RustFS bucket event notifications and deliver filtered object events to external targets." +--- + +RustFS Event Notifications deliver S3 bucket activity to external systems. This guide configures a webhook target, associates it with a bucket, and verifies delivery with AWS CLI. + +## Overview + +An event notification has two independent parts: + +- A **notification target** defines where RustFS sends events. +- A **bucket notification configuration** selects the target, event types, and optional object-key filters for one bucket. + +RustFS supports webhook, Kafka, MQTT, MySQL, PostgreSQL, NATS, Redis, AMQP, and Pulsar target families. The webhook workflow below is useful for HTTP services, serverless functions, and integration platforms. + +Bucket rules use the S3 `PutBucketNotificationConfiguration` and `GetBucketNotificationConfiguration` APIs. RustFS currently accepts queue configurations; AWS SNS topic and Lambda function configurations are not supported. + +Common event families include: + +| Event family | Example events | +| --- | --- | +| Object creation | `s3:ObjectCreated:Put`, `s3:ObjectCreated:Copy`, `s3:ObjectCreated:CompleteMultipartUpload` | +| Object removal | `s3:ObjectRemoved:Delete`, `s3:ObjectRemoved:DeleteMarkerCreated` | +| Object access | `s3:ObjectAccessed:Get`, `s3:ObjectAccessed:Head` | +| Object tagging | `s3:ObjectTagging:Put`, `s3:ObjectTagging:Delete` | +| Lifecycle and tiering | Expiration, restore, replication, and transition events | + +Use a wildcard such as `s3:ObjectCreated:*` to subscribe to every event in a family. + +## Configuration + +Event Notifications are disabled by default. The following environment variables enable the module and create a webhook target named `primary`: + +```bash +export RUSTFS_NOTIFY_ENABLE="true" +export RUSTFS_NOTIFY_WEBHOOK_ENABLE_PRIMARY="on" +export RUSTFS_NOTIFY_WEBHOOK_ENDPOINT_PRIMARY="https://events.example.com/rustfs" +export RUSTFS_NOTIFY_WEBHOOK_AUTH_TOKEN_PRIMARY="" +export RUSTFS_NOTIFY_WEBHOOK_QUEUE_DIR_PRIMARY="/var/lib/rustfs/notify-primary" +export RUSTFS_OUTBOUND_ALLOW_ORIGINS="https://events.example.com" +``` + +Restart RustFS after changing environment-managed targets. The target name is the lowercase form of the environment-variable suffix, so `_PRIMARY` creates `primary`. Use the target ARN `arn:rustfs:sqs:us-east-1:primary:webhook` in the bucket configuration. + +RustFS sends a `HEAD` health check to the endpoint origin's root path, such as `https://events.example.com/`, and sends JSON `POST` requests to the complete configured endpoint. The origin must respond to `HEAD`, while delivery requests must return a successful HTTP status. Allow the RustFS service account to write to the queue directory. + +### Webhook settings + +Use the same `_PRIMARY` suffix on all settings for this target: + +| Variable | Purpose | +| --- | --- | +| `RUSTFS_NOTIFY_WEBHOOK_ENDPOINT_PRIMARY` | HTTP or HTTPS delivery endpoint | +| `RUSTFS_NOTIFY_WEBHOOK_AUTH_TOKEN_PRIMARY` | Bearer token sent in the `Authorization` header | +| `RUSTFS_NOTIFY_WEBHOOK_QUEUE_DIR_PRIMARY` | Directory for queued deliveries | +| `RUSTFS_NOTIFY_WEBHOOK_QUEUE_LIMIT_PRIMARY` | Maximum number of queued events | +| `RUSTFS_NOTIFY_WEBHOOK_CLIENT_CERT_PRIMARY` | Client certificate for mutual TLS (mTLS) | +| `RUSTFS_NOTIFY_WEBHOOK_CLIENT_KEY_PRIMARY` | Private key paired with the client certificate | +| `RUSTFS_NOTIFY_WEBHOOK_CLIENT_CA_PRIMARY` | Certificate authority used to verify the receiver | +| `RUSTFS_NOTIFY_WEBHOOK_SKIP_TLS_VERIFY_PRIMARY` | Disable server certificate verification | + +:::warning[Keep certificate verification enabled] + +Do not enable `RUSTFS_NOTIFY_WEBHOOK_SKIP_TLS_VERIFY_PRIMARY` in production. Configure `RUSTFS_NOTIFY_WEBHOOK_CLIENT_CA_PRIMARY` when the receiver uses a private certificate authority. If you use mTLS, provide both the client certificate and private key. + +::: + +For all target families and target-specific keys, see [Environment Variables](/reference/environment-variables#event--audit-targets). + +## Usage + +The following example sends creation and removal events for `.dat` objects under `uploads/` to the `primary` webhook. + +### Configure the bucket rule + +Create the notification configuration: + +```json title="notification.json" +{ + "QueueConfigurations": [ + { + "Id": "primary-uploads", + "QueueArn": "arn:rustfs:sqs:us-east-1:primary:webhook", + "Events": [ + "s3:ObjectCreated:*", + "s3:ObjectRemoved:*" + ], + "Filter": { + "Key": { + "FilterRules": [ + { "Name": "prefix", "Value": "uploads/" }, + { "Name": "suffix", "Value": ".dat" } + ] + } + } + } + ] +} +``` + +Apply it to `my-bucket` with [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html): + +```bash +aws --endpoint-url http://localhost:9000 \ + --region us-east-1 \ + s3api put-bucket-notification-configuration \ + --bucket my-bucket \ + --notification-configuration file://notification.json +``` + +Saving a bucket rule does not prove that its target exists or is online. Confirm that the module and target are enabled, the receiver answers health checks, and a test object operation produces a delivery. + +Read the active configuration: + +```bash +aws --endpoint-url http://localhost:9000 \ + --region us-east-1 \ + s3api get-bucket-notification-configuration \ + --bucket my-bucket +``` + +Prefix and suffix filters are case-sensitive and are combined with AND logic. A rule can contain at most one prefix and one suffix filter. + +### Manage rules with rc + +The `rc` client can add, list, and remove bucket notification rules. It does not configure the notification module or create the underlying webhook, broker, or database target; configure those first through environment variables or the Console. + +Add creation and removal events for the `primary` target: + +```bash +rc bucket event add \ + rustfs/my-bucket \ + arn:rustfs:sqs:us-east-1:primary:webhook \ + --event put,delete +``` + +List the active rules: + +```bash +rc --json bucket event list rustfs/my-bucket +``` + +Remove every rule associated with that target ARN: + +```bash +rc bucket event remove \ + rustfs/my-bucket \ + arn:rustfs:sqs:us-east-1:primary:webhook +``` + +The current `rc bucket event add` command does not expose prefix or suffix filter options. Use AWS CLI when the rule must filter object keys. + +### Trigger and inspect events + +Upload and remove a matching object: + +```bash +printf 'hello from RustFS\n' > hello.txt + +aws --endpoint-url http://localhost:9000 \ + --region us-east-1 \ + s3 cp hello.txt s3://my-bucket/uploads/hello.dat + +aws --endpoint-url http://localhost:9000 \ + --region us-east-1 \ + s3 rm s3://my-bucket/uploads/hello.dat +``` + +The webhook receives an S3-compatible JSON envelope. Important fields include the event name, bucket, object key, size, ETag, and request identifiers: + +```json +{ + "EventName": "s3:ObjectCreated:Put", + "Records": [ + { + "eventName": "s3:ObjectCreated:Put", + "s3": { + "bucket": { "name": "my-bucket" }, + "object": { "key": "uploads%2Fhello.dat" } + } + } + ] +} +``` + +Decode the object key as URL-encoded data before using it. Consumers should also be idempotent: queued delivery provides store-and-forward recovery, but a receiver can observe the same event more than once. + +### Replace or remove rules + +`PutBucketNotificationConfiguration` replaces the complete notification configuration for the bucket. Include every rule that you want to retain when updating it. + +Remove all rules by applying an empty configuration: + +```bash +aws --endpoint-url http://localhost:9000 \ + --region us-east-1 \ + s3api put-bucket-notification-configuration \ + --bucket my-bucket \ + --notification-configuration '{}' +``` + +## Next steps + +Configure [Audit Targets](/security-compliance/audit-logs) when you need request-level security and compliance records instead of bucket-scoped object events. Review [Observability](/operations/observability) to monitor the RustFS service. \ No newline at end of file diff --git a/content/operations/high-availability/index.md b/content/operations/high-availability/index.md new file mode 100644 index 00000000..d274c255 --- /dev/null +++ b/content/operations/high-availability/index.md @@ -0,0 +1,7 @@ +--- +title: "High Availability" +description: "Review RustFS site replication and node recovery guidance." +--- + +- [Site Replication](./site-replication.md) +- [Node Healing](./node-healing.md) \ No newline at end of file diff --git a/content/operations/high-availability/meta.json b/content/operations/high-availability/meta.json new file mode 100644 index 00000000..350cc1f0 --- /dev/null +++ b/content/operations/high-availability/meta.json @@ -0,0 +1,7 @@ +{ + "title": "High Availability", + "pages": [ + "site-replication", + "node-healing" + ] +} \ No newline at end of file diff --git a/content/operations/high-availability/node-healing.md b/content/operations/high-availability/node-healing.md new file mode 100644 index 00000000..ef7bbd8f --- /dev/null +++ b/content/operations/high-availability/node-healing.md @@ -0,0 +1,270 @@ +--- +title: "Node Healing" +description: "Recover failed RustFS nodes and disks, monitor automatic healing, and run bounded manual heal tasks with rc." +--- + +RustFS healing reconstructs missing or inconsistent erasure-coded data from the healthy shards that remain in a storage set. Use this workflow after restoring an unavailable node, replacing a failed disk, or investigating scanner and heal alerts. + +Healing is a cluster storage operation. It is different from [site replication](/operations/high-availability/site-replication), which copies data and identity configuration between independent RustFS deployments. + +:::warning[Healing is not a backup] + +RustFS can reconstruct data only while enough healthy shards remain in the affected erasure set. Healing cannot recover an object after failures exceed the set's available redundancy, and it does not protect against intentional deletion or application-level corruption. Maintain independent backups or replication according to your recovery requirements. + +::: + +## How healing works + +RustFS coordinates several healing sources through the cluster heal queue: + +- `autoHeal`: The automatic disk scanner detects disks that are unformatted or have returned after an outage and queues erasure-set healing. +- `scanner`: The background data scanner can queue object and bucket healing when it finds inconsistent storage state. +- `readRepair`: Read-repair paths can submit work discovered while serving requests. +- `internal`: Internal recovery paths can submit system-initiated work. +- `admin`: Administrators can submit a recursive, bucket, or prefix heal with `rc admin heal start`. + +Automatic disk healing runs at low priority. The `healOperations` status object uses these source names to separate queued, active, and retrying work. + +:::note[Reconstruction consumes cluster resources] + +Healing reads healthy shards and writes reconstructed data. Deep scans add read and checksum work. Monitor client latency, disk utilization, available capacity, and the heal queue while recovery is active. + +::: + +## Requirements + +Before recovering a node or disk, prepare: + +- A RustFS deployment that still has enough healthy shards to reconstruct the affected data. +- Stable node addresses and the original configured storage mount paths. +- Replacement storage with capacity appropriate for the affected erasure set. +- The RustFS [`rc`](/operations/rc) client on a secured administration host. +- Administrative credentials that permit cluster information, scanner, and heal operations. +- A maintenance window when replacing hardware or running a deep recursive scan. + +Record the failed node, pool, set, disk endpoint, mount path, and file-system UUID before changing hardware. Do not remove additional disks from the same erasure set while recovery is incomplete. + +## 1. Identify the failed component + +Configure an `rc` alias if you do not already have one: + +```bash +rc alias set rustfs https://rustfs.example.com:9000 \ + \ + --region us-east-1 --bucket-lookup path +``` + +Check cluster readiness, topology, storage health, and heal activity: + +```bash +rc ready rustfs +rc admin info cluster rustfs +rc admin info storage rustfs +rc admin scanner status rustfs +rc admin heal status rustfs +``` + +Readiness can fail while the cluster is degraded. Use the administrative information commands to identify which node or disk is unavailable and note its pool and erasure-set location. + +For machine-readable output, request JSON: + +```bash +rc --json admin info storage rustfs +rc --json admin heal status rustfs +``` + +The aggregate heal status reports runtime state, queued and active tasks, retrying work, request sources, priorities, and available progress counters. A zero-length queue does not prove that an offline disk has been replaced or that every object is recoverable; correlate it with storage topology and readiness. + +## 2. Restore the node or disk + +Correct the underlying failure before starting a manual heal. + +For a node outage: + +1. Restore the node's configured network identity and connectivity to every other RustFS node on the S3 API port, normally `9000`. +2. Mount the original data disks at their configured paths. +3. Confirm that the RustFS process uses the same storage endpoint configuration as the rest of the cluster. +4. Start RustFS and verify that the node rejoins the cluster. + +For a failed disk: + +1. Stop the RustFS process on the affected node before removing or remounting storage. +2. Replace the failed device and prepare its file system according to [Disk Preparation](/installation/requirement/disk-preparation). +3. Mount the replacement at the exact path used by the failed disk and update `/etc/fstab` with its file-system UUID. +4. Verify the mount, file-system type, ownership, and available capacity before starting RustFS. +5. Start RustFS and confirm that the disk appears in the expected pool and erasure set. + +```bash +findmnt /data/rustfs0 +df -hT /data/rustfs0 +rc admin info storage rustfs +``` + +:::warning[Do not reuse stale storage blindly] + +Confirm the identity and contents of every device before mounting it at a RustFS data path. Do not copy internal metadata between disks, combine multiple configured endpoints on one replacement file system, or mount a replacement over a path that still contains required data. + +::: + +## 3. Monitor automatic healing + +After the node or replacement disk is online, RustFS can detect the recovery candidate and queue erasure-set healing. Check both storage state and aggregate heal activity: + +```bash +rc admin info storage rustfs +rc admin heal status rustfs +rc admin scanner status rustfs +``` + +In JSON output, watch these groups: + +| Field | Meaning | +| --- | --- | +| `state` | Heal runtime state, such as `active` or `idle`. | +| `healQueueLength` | Total work waiting in the heal queue. | +| `healActiveTasks` | Total heal tasks currently running. | +| `healOperations` | Queue, active, and retry counts grouped by source (`scanner`, `admin`, `autoHeal`, `internal`, and `readRepair`) and priority. | +| `progress` | Available totals for objects scanned, healed, failed, and bytes processed. | +| `clusterStatusComplete` | Whether the response includes every expected node. | + +Do not treat `idle` as successful recovery by itself. Confirm that the replacement disk is online, no heal failures remain, the cluster is ready, and representative objects are readable. + +## 4. Run a targeted manual heal + +Use a manual task when automatic recovery does not cover the required path or when you need an explicit scan after an incident. Start with the narrowest bucket or prefix that contains affected data. + +Preview a bucket heal without applying changes: + +```bash +rc admin heal start rustfs \ + --bucket my-bucket \ + --scan-mode normal \ + --dry-run +``` + +Run the targeted heal after reviewing the preview: + +```bash +rc admin heal start rustfs \ + --bucket my-bucket \ + --scan-mode normal +``` + +Limit the task to an object prefix when the affected scope is known: + +```bash +rc admin heal start rustfs \ + --bucket my-bucket \ + --prefix archive/ \ + --scan-mode deep +``` + +`normal` is the default scan mode. Use `deep` when you need deeper data verification and have planned for the additional disk I/O. Omit `--bucket` only when a recursive scan of all buckets is necessary. + +:::warning[Review mutation options separately] + +The `--remove` option removes dangling objects or parts, and `--recreate` requests recreation of missing data. Do not add either option to an incident command until you have inspected the affected storage state, tested with `--dry-run`, and confirmed the intended result. + +::: + +## 5. Track the manual task + +Every manual heal returns a `clientToken`. Save it with the incident record because status and stop operations for that task require the token. + +For a bucket or prefix task, pass the original target and token: + +```bash +rc admin heal status rustfs \ + --bucket my-bucket \ + --prefix archive/ \ + --client-token +``` + +For a recursive task started without `--bucket`, use the token alone: + +```bash +rc admin heal status rustfs --client-token +``` + +The tokenless command remains the cluster-wide background summary: + +```bash +rc admin heal status rustfs +``` + +Task status can include scanned, healed, and failed item totals, bytes processed, the current item, scan mode, start time, and last update. Investigate failed items rather than relying only on the task's aggregate state. + +## 6. Validate recovery + +After the heal queue drains, verify the cluster and the recovered data path: + +```bash +rc ready rustfs +rc admin info cluster rustfs +rc admin info storage rustfs +rc admin heal status rustfs +``` + +Read representative objects from affected buckets and versions. For a known test object: + +```bash +rc object stat rustfs/my-bucket/hello.txt +rc object copy rustfs/my-bucket/hello.txt ./hello-recovered.txt +``` + +Compare the downloaded object with an independent checksum or trusted source when one is available. A successful object read validates that request, not every object or every shard in the cluster. + +Continue monitoring storage health and heal failures after the cluster becomes ready. Preserve the incident timeline, failed-device information, task token, and final status output for review. + +## 7. Stop a manual heal + +Stop only the task you have identified. For a bucket or prefix task, include the original target and token: + +```bash +rc admin heal stop rustfs \ + --bucket my-bucket \ + --prefix archive/ \ + --client-token +``` + +Stop a recursive token-scoped task with: + +```bash +rc admin heal stop rustfs --client-token +``` + +The tokenless stop command targets the global background heal operation: + +```bash +rc admin heal stop rustfs +``` + +Use the global stop only when continued background work creates a larger operational risk. Stopping healing leaves recovery incomplete; record the reason and confirm how healing will resume before closing the incident. + +## Troubleshooting + +### The replacement disk is not healing + +Confirm that the replacement is mounted at the configured endpoint and appears in `rc admin info storage`. Check the RustFS logs for disk identification, formatting, permission, or queue-admission failures. Also confirm that healing services are initialized with `rc admin heal status`. + +### Heal status is partial + +If `clusterStatusComplete` is false, one or more expected nodes did not contribute to the aggregate response. Restore node-to-node connectivity and check each node's storage and process health before trusting cluster totals. + +### The queue remains active + +Compare queued, active, and retrying work by source. A growing `autoHeal` count points to disk or erasure-set recovery; scanner work points to background discovery; admin work belongs to manually submitted tasks. Check disk latency, free capacity, node connectivity, and repeated item failures before starting another task. + +### Objects cannot be reconstructed + +Stop additional maintenance on the affected erasure set. Preserve the remaining disks and logs, then determine whether enough healthy shards survive. Repeated deep scans cannot reconstruct data when the available redundancy has already been exceeded. + +### A manual task cannot be queried + +Use the `clientToken` returned by `heal start`. Bucket and prefix tasks must be queried with the same `--bucket` and `--prefix` target used at start; a recursive root task uses the token without a bucket. + +## Next steps + +- Review [Status Check](/operations/status-check) for readiness, cluster, and capacity checks. +- Configure [Observability](/operations/observability) to alert on disk health and sustained recovery work. +- Review [Site Replication](/operations/high-availability/site-replication) when recovery requires an independent RustFS deployment. diff --git a/content/operations/high-availability/site-replication.md b/content/operations/high-availability/site-replication.md new file mode 100644 index 00000000..fb7ff8a1 --- /dev/null +++ b/content/operations/high-availability/site-replication.md @@ -0,0 +1,302 @@ +--- +title: "Site Replication" +description: "Configure and operate multi-site RustFS replication with the rc command-line client." +--- + +Site replication links two or more independent **RustFS** deployments. It synchronizes buckets, object versions, and supported identity and access management (IAM) data across the linked sites. Use this workflow when applications need to write to separate RustFS sites while keeping their storage and identity configuration aligned. + +Site replication is different from [bucket replication](/administration/data/bucket/replication). Bucket replication is a directional rule between selected buckets; site replication establishes a broader relationship between complete RustFS deployments. + +:::warning[Plan recovery separately] + +Site replication is asynchronous. A successful write at one site does not confirm that another site already has the replicated version. Site replication also does not provide DNS failover, traffic routing, or application recovery orchestration. + +::: + +## Replicated resources + +RustFS site replication synchronizes these resource families across linked sites: + +- Buckets and object versions, including delete markers. +- Bucket metadata required by the site replication workflow. +- IAM users, groups, policies, policy mappings, and service accounts. + +Bucket versioning support is required at every participating site. RustFS configures the site relationship and the underlying replication targets when you add the sites. + +## Requirements + +Before linking sites, prepare: + +- Two or more independent RustFS deployments running a release that exposes the site replication Admin API. +- A unique, stable S3 API endpoint for each deployment. +- Bidirectional network connectivity between every site endpoint on the S3 API port, normally `9000`. +- Trusted TLS certificates for production endpoints. +- The RustFS [`rc`](/operations/rc) client on a secured administration host. +- Root administrative credentials for every participating site. +- Compatible bucket-versioning support on every site. + +Use empty test sites when first validating the workflow. Before linking existing sites, inventory bucket names, object-lock settings, IAM identities, and policies for conflicts. + +:::warning[Protect administrative credentials] + +The `add` operation resolves every peer endpoint and root credential from your local `rc` alias store. Run it only from a trusted administration host, restrict access to the local `rc` configuration, and do not place credentials in shell history, logs, or screenshots. + +::: + +## 1. Configure site aliases + +Configure one alias for each site. Replace the endpoint and credentials for both deployments: + +```bash +rc alias set site1 https://site1.example.com:9000 \ + \ + --region us-east-1 --bucket-lookup path + +rc alias set site2 https://site2.example.com:9000 \ + \ + --region us-east-1 --bucket-lookup path +``` + +Check that both sites are reachable and ready: + +```bash +rc ready site1 +rc ready site2 +rc admin info cluster site1 +rc admin info cluster site2 +``` + +Use HTTPS endpoints in production. If a site uses a private certificate authority, install that authority in the administration host's trust store before continuing. + +:::warning[Loopback endpoints are for tests only] + +RustFS rejects loopback replication targets by default as an outbound-request security control. The server option `RUSTFS_REPLICATION_ALLOW_LOOPBACK_TARGET=true` exists only for single-host development and automated tests. Never enable it in production. + +::: + +## 2. Link the sites + +Submit all participating aliases in one command. The first alias receives the administration request: + +```bash +rc admin replicate add site1 site2 +``` + +To create a relationship with more than two sites, include every alias in the initial command: + +```bash +rc admin replicate add site1 site2 site3 +``` + +Do not run separate pairwise `add` commands to build the same multi-site relationship. + +Inspect the resulting configuration from at least two sites: + +```bash +rc admin replicate info site1 +rc admin replicate info site2 +``` + +The output identifies each linked deployment by its deployment ID, site name, and endpoint. Save the deployment IDs because edit and resync operations can select a site by exact deployment ID or exact unique name. + +## 3. Check replication status + +Request the default bucket and IAM summaries: + +```bash +rc admin replicate status site1 +``` + +Select specific status sections when investigating an issue: + +```bash +rc admin replicate status site1 --buckets +rc admin replicate status site1 --users --groups --policies +rc admin replicate status site1 --metrics +``` + +Check status from each site. A healthy response from `site1` confirms that site's view of the relationship; it does not replace checking the other deployments. + +## 4. Validate data and IAM replication + +Create a test bucket and object at the first site: + +```bash +rc bucket create site1/my-bucket +printf 'hello rustfs\n' > hello.txt +rc object copy hello.txt site1/my-bucket/hello.txt +``` + +Poll the second site until the object appears: + +```bash +rc object stat site2/my-bucket/hello.txt +rc object copy site2/my-bucket/hello.txt ./hello-from-site2.txt +``` + +Compare the downloaded content before treating the data path as validated: + +```bash +cmp hello.txt hello-from-site2.txt +``` + +For IAM validation, create a temporary user and attach a test policy at one site, then inspect the user and policy mapping at another site. Remove the temporary identity after validation: + +```bash +rc admin user add site1 replication-test +rc admin policy attach site1 readonly --user replication-test + +rc admin user info site2 replication-test +rc admin policy entities site2 --user replication-test + +rc admin user rm site1 replication-test +``` + +Use a randomly generated temporary secret and do not reuse an application or administrator credential. + +## 5. Edit a site + +Use `edit` when a linked site's name, endpoint, or TLS trust settings change. First inspect the current deployment IDs and names: + +```bash +rc admin replicate info site1 +``` + +Rename a site: + +```bash +rc admin replicate edit site1 \ + --site \ + --name site2-dr \ + --yes +``` + +Replace an endpoint and keep TLS verification enabled: + +```bash +rc admin replicate edit site1 \ + --site site2-dr \ + --endpoint https://site2-dr.example.com:9000 \ + --verify-tls \ + --yes +``` + +For a private certificate authority, provide a certificate-only PEM bundle: + +```bash +rc admin replicate edit site1 \ + --site site2-dr \ + --ca-cert ./site2-ca.pem \ + --yes +``` + +Clear a previously configured custom CA when the endpoint moves to a certificate trusted by the system trust store: + +```bash +rc admin replicate edit site1 \ + --site site2-dr \ + --clear-ca-cert \ + --verify-tls \ + --yes +``` + +The endpoint must be an HTTP or HTTPS origin without a path, query, fragment, or embedded credentials. Prefer a trusted certificate over `--skip-tls-verify`. + +## 6. Resynchronize a site + +Use resynchronization after repairing a site that was unavailable or when status checks show missing replicated state. Start a resync for one exact peer: + +```bash +rc admin replicate resync start site1 \ + --site \ + --yes +``` + +Read the last persisted resync snapshot: + +```bash +rc admin replicate resync status site1 \ + --site +``` + +Cancel the selected resync only when you have confirmed it should stop: + +```bash +rc admin replicate resync cancel site1 \ + --site \ + --yes +``` + +:::warning[Resync status is not live progress] + +The current RustFS resync status endpoint returns the persisted result of the most recent start or cancel request. It does not inspect live workers, and its lifecycle state is reported as unknown. Start operations can overlap, cancellation is not idempotent, and a network timeout can leave the mutation outcome unknown. After an ambiguous result, inspect `replicate info`, `replicate status`, and the target data before issuing another mutation. + +::: + +## 7. Remove site replication + +Removing a site changes the replication relationship. It does not provide an application cutover or prove that every queued object has arrived. Check status and preserve any required data before proceeding. + +Remove one named site: + +```bash +rc admin replicate remove site1 --site site2-dr +``` + +Repeat `--site` to remove multiple named sites in one request: + +```bash +rc admin replicate remove site1 \ + --site site2-dr \ + --site site3 +``` + +Dissolve the complete site replication relationship: + +```bash +rc admin replicate remove site1 --all +``` + +Verify the result from the remaining deployments: + +```bash +rc admin replicate info site1 +rc admin replicate status site1 +``` + +## Troubleshooting + +### A peer endpoint is rejected + +Confirm that the endpoint is a reachable HTTP or HTTPS origin and does not resolve to loopback. For production, make every site reachable through its stable network address and keep the loopback override disabled. + +### TLS validation fails + +Confirm that the endpoint certificate contains the peer hostname and that the issuing certificate authority is trusted. Use `replicate edit --ca-cert` for a private CA bundle. Do not make `--skip-tls-verify` the permanent fix. + +### A mutation times out + +Do not immediately repeat `add`, `edit`, `resync start`, `resync cancel`, or `remove`. The server may have applied the change before the client lost the response. Inspect the relationship and data first: + +```bash +rc admin replicate info site1 +rc admin replicate status site1 +``` + +### Replication falls behind + +Check the site status and metrics from every deployment, then verify endpoint reachability and storage readiness: + +```bash +rc admin replicate status site1 --metrics +rc ready site1 +rc ready site2 +``` + +Application writes can outpace asynchronous replication. Do not infer a recovery point objective from a single status sample; measure lag under your workload and alert on sustained backlog. + +## Next steps + +- Review [Bucket Replication](/administration/data/bucket/replication) when you need directional replication for selected buckets instead of a complete site relationship. +- Monitor [cluster status and readiness](/operations/status-check) at every site. +- Review the [`rc` command guide](/operations/rc) and keep the client aligned with the RustFS server release. \ No newline at end of file diff --git a/content/operations/index.md b/content/operations/index.md new file mode 100644 index 00000000..d43ac1b8 --- /dev/null +++ b/content/operations/index.md @@ -0,0 +1,15 @@ +--- +title: "Operations & Observability" +description: "Operate, monitor, scale, upgrade, and troubleshoot RustFS deployments." +--- + +Use this section to keep RustFS deployments healthy through upgrades, scaling, availability planning, monitoring, and incident response. + +## Operational areas + +- [Upgrade](./upgrade/index.md) covers binary, container, and Kubernetes upgrades. +- [Scaling](./scaling/index.md) covers storage expansion, rebalancing, and decommissioning. +- [High Availability](./high-availability/index.md) covers node healing and site replication. +- [Observability](./observability.md) covers metrics, logs, traces, monitoring integrations, and alert signals. +- [Status Check](./status-check.md) covers network ports, health probes, cluster status, and storage capacity checks. +- [Event Notifications](./event-notifications.md) covers bucket event targets. \ No newline at end of file diff --git a/content/operations/meta.json b/content/operations/meta.json new file mode 100644 index 00000000..72a98c46 --- /dev/null +++ b/content/operations/meta.json @@ -0,0 +1,13 @@ +{ + "title": "Operations & Observability", + "defaultOpen": true, + "pages": [ + "rc", + "upgrade", + "scaling", + "high-availability", + "observability", + "status-check", + "event-notifications" + ] +} \ No newline at end of file diff --git a/content/operations/monitoring.md b/content/operations/monitoring.md deleted file mode 100644 index 8f565bd1..00000000 --- a/content/operations/monitoring.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: "Monitoring and Alerting" -description: "This article explains how RustFS exports metrics, traces, and logs over OTLP, how to wire the pipeline into Prometheus and Grafana, which health endpoints to probe, and which signals to alert on." ---- - -## How RustFS Exposes Telemetry - -RustFS **pushes** metrics, traces, and logs over OTLP (OpenTelemetry Protocol). It does **not** expose a native Prometheus `/metrics` scrape endpoint — there is no HTTP route on port 9000 or 9001 that serves Prometheus text format. To get RustFS metrics into Prometheus you must run an OpenTelemetry Collector that receives OTLP from RustFS and re-exposes the data in Prometheus format: - -```text -RustFS (RUSTFS_OBS_ENDPOINT) --OTLP--> OpenTelemetry Collector --> Prometheus --> Grafana - \--> Loki (logs) - \--> Tempo / Jaeger (traces) -``` - -Point RustFS at the Collector with: - -```bash -# OTLP over HTTP (the Collector's default HTTP receiver port is 4318; gRPC is 4317) -RUSTFS_OBS_ENDPOINT=http://otel-collector:4318 -``` - -Related environment variables (all defined in the server configuration): - -| Variable | Purpose | -| --- | --- | -| `RUSTFS_OBS_ENDPOINT` | Base OTLP endpoint for traces, metrics, and logs | -| `RUSTFS_OBS_TRACE_ENDPOINT` / `RUSTFS_OBS_METRIC_ENDPOINT` / `RUSTFS_OBS_LOG_ENDPOINT` | Per-signal endpoint overrides | -| `RUSTFS_OBS_METRICS_EXPORT_ENABLED` / `RUSTFS_OBS_TRACES_EXPORT_ENABLED` / `RUSTFS_OBS_LOGS_EXPORT_ENABLED` | Toggle each signal | -| `RUSTFS_OBS_METER_INTERVAL` | Metric export interval | -| `RUSTFS_OBS_SERVICE_NAME` / `RUSTFS_OBS_ENVIRONMENT` | Resource attributes attached to exported telemetry | -| `RUSTFS_OBS_LOGGER_LEVEL` | Log verbosity (e.g. `info`) | - -## Reference Deployment (Docker Compose Observability Profile) - -The upstream `rustfs/rustfs` repository ships a complete reference stack in `docker-compose.yml` behind the `observability` profile: **otel-collector**, **Prometheus**, **Grafana**, **Loki** (logs), **Tempo** and **Jaeger** (traces). Its Collector configuration receives OTLP on 4317 (gRPC) and 4318 (HTTP) and re-exports metrics for Prometheus on port `8889`: - -```yaml -# OpenTelemetry Collector (excerpt from the upstream reference config) -receivers: - otlp: - protocols: - grpc: - endpoint: 0.0.0.0:4317 - http: - endpoint: 0.0.0.0:4318 -exporters: - prometheus: - endpoint: "0.0.0.0:8889" -service: - pipelines: - metrics: - receivers: [otlp] - exporters: [prometheus] -``` - -Prometheus then scrapes the Collector, not RustFS: - -```yaml -# prometheus.yml (excerpt) -scrape_configs: - - job_name: "rustfs-app-metrics" - static_configs: - - targets: ["otel-collector:8889"] # RustFS application metrics - - job_name: "otel-collector" - static_configs: - - targets: ["otel-collector:8888"] # Collector self-metrics -``` - -:::note -If Prometheus shows no `rustfs_*` series, check the chain in order: is `RUSTFS_OBS_ENDPOINT` set on every node, is the Collector reachable from the nodes, and is Prometheus scraping the Collector's `8889` exporter port. -::: - -## Health Endpoints - -The S3 listener on port **9000** serves the probe endpoints; the console on port **9001** has its own health path. - -| Endpoint | Port | Meaning | -| --- | --- | --- | -| `GET /health`, `GET /health/live` | 9000 | Liveness — returns `200` as long as the process is up | -| `GET /health/ready` | 9000 | Readiness — `200` only when storage, IAM, and peer health are ready; `503` otherwise | -| `GET /minio/health/ready` | 9000 | MinIO-compatible alias of the readiness probe | -| `GET /minio/health/cluster`, `GET /minio/health/cluster/read` | 9000 | Cluster write/read health (additionally require lock quorum) | -| `GET /rustfs/console/health` | 9001 | Console process health | - -A ready node answers `200` with `"ready": true`. A degraded node answers `503` with per-dependency detail: - -```bash -curl -s http://:9000/health/ready | jq -``` - -The `details` object reports `storage` / `iam` / `lock` (and `kms` when configured), and `degradedReasons` lists machine-readable causes such as `storage_quorum_unavailable`, `iam_not_ready`, or `lock_quorum_unavailable`. See [Cold Start and Quorum Loss](./cold-start) for the full degraded-startup semantics. - -## Key Metrics - -All metric names below are exported via OTLP and appear in Prometheus after the Collector re-exports them (label set as recorded in the server source; the Collector may add resource labels). See the [Metrics Reference](../reference/metrics) for the full verified list. - -| Metric | Type | What it tells you | -| --- | --- | --- | -| `rustfs_runtime_readiness_ready` | gauge | `1` when the node is fully ready, `0` while degraded | -| `rustfs_runtime_readiness_degraded_total` | counter (`reason`) | Degraded-readiness evaluations by reason | -| `rustfs_scanner_objects_scanned_total` | counter | Objects visited by the background scanner | -| `rustfs_scanner_cycles_total` | counter (`result`: success/error/partial) | Completed scanner cycles | -| `rustfs_scanner_cycle_duration_seconds` | gauge | Duration of the last scanner cycle | -| `rustfs_heal_task_running` | gauge (`type`, `set`) | Currently running heal tasks per erasure set | -| `rustfs_heal_queue_delay_seconds` | histogram (`type`, `set`) | Time heal tasks spend queued before starting | -| `rustfs_heal_task_start_total` | counter (`type`, `set`) | Heal tasks started | -| `rustfs_capacity_current_bytes` | gauge | Current used capacity in bytes | -| `rustfs_start_total` | counter | Process starts (spikes indicate restart loops) | - -## Suggested Alerts - -| Alert | Signal | Suggested rule | -| --- | --- | --- | -| Node not ready > 2 min | `rustfs_runtime_readiness_ready` gauge, or an external probe on `GET /health/ready` returning non-200 | `rustfs_runtime_readiness_ready == 0` for `2m` | -| Restart loop | `rustfs_start_total` | `increase(rustfs_start_total[10m]) > 3` | -| Heal backlog growing | `rustfs_heal_task_running` and `rustfs_heal_queue_delay_seconds` | sustained non-zero running tasks plus rising queue delay for `30m` | -| Scanner stalled | `rustfs_scanner_cycles_total` | `increase(rustfs_scanner_cycles_total[24h]) == 0` | -| Cluster used capacity watermark | `rustfs_capacity_current_bytes` | compare against your deployed raw capacity (a constant you set), e.g. `rustfs_capacity_current_bytes > 0.8 * ` | - -:::note -Per-disk usage and disk online/offline state are currently surfaced through the console and the admin API (server/storage info), not as dedicated OTLP metrics. For a per-disk watermark alert, poll the admin API from your own exporter or watch the console dashboard. -::: - -## Log Collection - -Where logs go is controlled by `RUSTFS_OBS_LOG_DIRECTORY`: - -- **Unset** — logs go to stdout. In containers, use your log driver; on systemd hosts, stdout/stderr is captured by **journald**, so `journalctl -u rustfs -f` works without extra configuration. -- **A local directory** (e.g. `/logs`) — RustFS writes rotating log files there. Rotation is tuned with `RUSTFS_OBS_LOG_FILENAME`, `RUSTFS_OBS_LOG_ROTATION_SIZE_MB`, `RUSTFS_OBS_LOG_ROTATION_TIME`, and `RUSTFS_OBS_LOG_KEEP_FILES`. -- **A URL** (contains `://`) — logs are shipped to a remote endpoint. - -When `RUSTFS_OBS_ENDPOINT` is set and log export is enabled, logs are additionally exported via OTLP; the reference stack routes them into Loki for querying from Grafana. - -```bash title="Example: file logs plus OTLP export" -RUSTFS_OBS_LOG_DIRECTORY=/var/log/rustfs -RUSTFS_OBS_LOGGER_LEVEL=info -RUSTFS_OBS_ENDPOINT=http://otel-collector:4318 -``` diff --git a/content/reference/metrics.md b/content/operations/observability.md similarity index 59% rename from content/reference/metrics.md rename to content/operations/observability.md index 016daef0..2496cb2e 100644 --- a/content/reference/metrics.md +++ b/content/operations/observability.md @@ -1,15 +1,83 @@ --- -title: "Metrics Reference" -description: "This article lists the RustFS metric names, types, and labels that operators can chart and alert on after wiring the OTLP export pipeline into Prometheus." +title: "Observability" +description: "This article explains RustFS telemetry exports, monitoring integrations, metric names and labels, recommended alerts, and log collection." --- -## How to Read This Page +## How RustFS Exposes Telemetry + +RustFS **pushes** metrics, traces, and logs over OTLP (OpenTelemetry Protocol). It does **not** expose a native Prometheus `/metrics` scrape endpoint — there is no HTTP route on port 9000 or 9001 that serves Prometheus text format. To get RustFS metrics into Prometheus you must run an OpenTelemetry Collector that receives OTLP from RustFS and re-exposes the data in Prometheus format: + +```text +RustFS (RUSTFS_OBS_ENDPOINT) --OTLP--> OpenTelemetry Collector --> Prometheus --> Grafana + \--> Loki (logs) + \--> Tempo / Jaeger (traces) +``` + +Point RustFS at the Collector with: + +```bash +# OTLP over HTTP (the Collector's default HTTP receiver port is 4318; gRPC is 4317) +RUSTFS_OBS_ENDPOINT=http://otel-collector:4318 +``` + +Related environment variables (all defined in the server configuration): + +| Variable | Purpose | +| --- | --- | +| `RUSTFS_OBS_ENDPOINT` | Base OTLP endpoint for traces, metrics, and logs | +| `RUSTFS_OBS_TRACE_ENDPOINT` / `RUSTFS_OBS_METRIC_ENDPOINT` / `RUSTFS_OBS_LOG_ENDPOINT` | Per-signal endpoint overrides | +| `RUSTFS_OBS_METRICS_EXPORT_ENABLED` / `RUSTFS_OBS_TRACES_EXPORT_ENABLED` / `RUSTFS_OBS_LOGS_EXPORT_ENABLED` | Toggle each signal | +| `RUSTFS_OBS_METER_INTERVAL` | Metric export interval | +| `RUSTFS_OBS_SERVICE_NAME` / `RUSTFS_OBS_ENVIRONMENT` | Resource attributes attached to exported telemetry | +| `RUSTFS_OBS_LOGGER_LEVEL` | Log verbosity (e.g. `info`) | + +## Reference Deployment (Docker Compose Observability Profile) + +The upstream `rustfs/rustfs` repository ships a complete reference stack in `docker-compose.yml` behind the `observability` profile: **otel-collector**, **Prometheus**, **Grafana**, **Loki** (logs), **Tempo** and **Jaeger** (traces). Its Collector configuration receives OTLP on 4317 (gRPC) and 4318 (HTTP) and re-exports metrics for Prometheus on port `8889`: + +```yaml +# OpenTelemetry Collector (excerpt from the upstream reference config) +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 +exporters: + prometheus: + endpoint: "0.0.0.0:8889" +service: + pipelines: + metrics: + receivers: [otlp] + exporters: [prometheus] +``` + +Prometheus then scrapes the Collector, not RustFS: + +```yaml +# prometheus.yml (excerpt) +scrape_configs: + - job_name: "rustfs-app-metrics" + static_configs: + - targets: ["otel-collector:8889"] # RustFS application metrics + - job_name: "otel-collector" + static_configs: + - targets: ["otel-collector:8888"] # Collector self-metrics +``` -RustFS exports metrics via OTLP push — there is no native `/metrics` scrape endpoint. Run an OpenTelemetry Collector with a Prometheus exporter and point `RUSTFS_OBS_ENDPOINT` at it; see [Monitoring and Alerting](../operations/monitoring) for the pipeline setup. +:::note + +If Prometheus shows no `rustfs_*` series, check the chain in order: is `RUSTFS_OBS_ENDPOINT` set on every node, is the Collector reachable from the nodes, and is Prometheus scraping the Collector's `8889` exporter port. + +::: + +## Metrics Reference -Every name below is verified against the server source. The list covers the operations-relevant subset exported via OTLP; internal and experimental instruments are omitted, and new releases may add metrics not listed here. Label sets are those recorded at the emission site — the Collector and Prometheus may attach additional resource labels (`service_name`, instance, and so on). +Every name below is verified against the server source. The list covers the operations-relevant subset exported via OTLP; internal and experimental instruments are omitted, and new releases may add metrics not listed here. Label sets are those recorded at the emission site. The Collector and Prometheus may attach additional resource labels, such as `service_name` and instance. -## Readiness and Process +### Readiness and Process | Metric | Type | Labels | Meaning | | --- | --- | --- | --- | @@ -17,7 +85,7 @@ Every name below is verified against the server source. The list covers the oper | `rustfs_runtime_readiness_ready` | gauge | — | `1` when the node is fully ready, `0` while degraded | | `rustfs_runtime_readiness_degraded_total` | counter | `reason` | Degraded readiness evaluations, by `degradedReasons` value (e.g. `storage_quorum_unavailable`) | -## Scanner +### Scanner | Metric | Type | Labels | Meaning | | --- | --- | --- | --- | @@ -33,7 +101,7 @@ Every name below is verified against the server source. The list covers the oper | `rustfs_scanner_set_scan_concurrency_limit` | gauge | — | Current per-set scan concurrency limit | | `rustfs_scanner_disk_scan_concurrency_limit` | gauge | `pool`, `set` | Current per-disk scan concurrency limit | -## Heal +### Heal | Metric | Type | Labels | Meaning | | --- | --- | --- | --- | @@ -50,7 +118,7 @@ Every name below is verified against the server source. The list covers the oper | `rustfs_heal_candidate_priority_reject_total` | counter | — | Candidates rejected by priority policy | | `rustfs_heal_read_repair_dedup_total` | counter | `reason` | Read-path repair requests deduplicated | -## API / IO Path +### API / IO Path | Metric | Type | Labels | Meaning | | --- | --- | --- | --- | @@ -70,7 +138,7 @@ Every name below is verified against the server source. The list covers the oper | `rustfs_io_scheduler_load` | counter | `level` | Scheduler decisions by load level | | `rustfs_io_load_changes` | counter | `from`, `to` | IO load-level transitions | -## Backpressure +### Backpressure | Metric | Type | Labels | Meaning | | --- | --- | --- | --- | @@ -80,7 +148,7 @@ Every name below is verified against the server source. The list covers the oper | `rustfs_backpressure_rejections` | counter | — | Requests rejected due to backpressure | | `rustfs_backpressure_concurrent` | gauge | — | Current concurrent requests tracked by the limiter | -## Capacity +### Capacity | Metric | Type | Labels | Meaning | | --- | --- | --- | --- | @@ -91,7 +159,7 @@ Every name below is verified against the server source. The list covers the oper | `rustfs_capacity_cache_misses` | counter | — | Capacity cache misses | | `rustfs_capacity_cache_served_total` | counter | `state` | Capacity responses served from cache, by freshness state | -## Locking +### Locking | Metric | Type | Labels | Meaning | | --- | --- | --- | --- | @@ -103,7 +171,7 @@ Every name below is verified against the server source. The list covers the oper | `rustfs_object_lock_diag_hold_duration_seconds` | histogram | — | Object-lock hold duration (diagnostics mode) | | `rustfs_object_lock_diag_slow_acquire_total` / `rustfs_object_lock_diag_slow_hold_total` | counter | — | Slow lock acquire/hold events (diagnostics mode) | -## Log Cleaner +### Log Cleaner | Metric | Type | Labels | Meaning | | --- | --- | --- | --- | @@ -114,6 +182,40 @@ Every name below is verified against the server source. The list covers the oper | `rustfs_log_cleaner_rotation_duration_seconds` | histogram | — | Rotation duration | | `rustfs_log_cleaner_active_file_size_bytes` | gauge | — | Size of the active log file | +:::note[Additional metrics] + +Additional verified metric names exist whose type/label details are not documented here, including `rustfs_scanner_cache_save_attempt_total`, `rustfs_scanner_cache_save_timeout_total`, `rustfs_scanner_cache_save_retry_total`, `rustfs_scanner_excess_object_versions_total`, `rustfs_scanner_excess_object_version_size_total`, `rustfs_scanner_excess_folders_total`, `rustfs_scanner_pending_heal_prune_total`, `rustfs_scanner_pending_heal_malformed_total`, and the `rustfs_io_scheduler_*` / `rustfs_io_buffer_*` / `rustfs_timeout_dynamic_*` families. Inspect them in Prometheus once the pipeline is live. + +::: + +## Suggested Alerts + +| Alert | Signal | Suggested rule | +| --- | --- | --- | +| Node not ready > 2 min | `rustfs_runtime_readiness_ready` gauge, or an external probe on `GET /health/ready` returning non-200 | `rustfs_runtime_readiness_ready == 0` for `2m` | +| Restart loop | `rustfs_start_total` | `increase(rustfs_start_total[10m]) > 3` | +| Heal backlog growing | `rustfs_heal_task_running` and `rustfs_heal_queue_delay_seconds` | sustained non-zero running tasks plus rising queue delay for `30m` | +| Scanner stalled | `rustfs_scanner_cycles_total` | `increase(rustfs_scanner_cycles_total[24h]) == 0` | +| Cluster used capacity watermark | `rustfs_capacity_current_bytes` | compare against your deployed raw capacity (a constant you set), e.g. `rustfs_capacity_current_bytes > 0.8 * ` | + :::note -Additional verified metric names exist whose type/label details are not documented here, including `rustfs_scanner_cache_save_attempt_total`, `rustfs_scanner_cache_save_timeout_total`, `rustfs_scanner_cache_save_retry_total`, `rustfs_scanner_excess_object_versions_total`, `rustfs_scanner_excess_object_version_size_total`, `rustfs_scanner_excess_folders_total`, `rustfs_scanner_pending_heal_prune_total`, `rustfs_scanner_pending_heal_malformed_total`, and the `rustfs_io_scheduler_*` / `rustfs_io_buffer_*` / `rustfs_timeout_dynamic_*` families. Inspect them in your Prometheus once the pipeline is live. + +Per-disk usage and disk online/offline state are currently surfaced through the console and the admin API (server/storage info), not as dedicated OTLP metrics. For a per-disk watermark alert, poll the admin API from your own exporter or watch the console dashboard. + ::: + +## Log Collection + +Where logs go is controlled by `RUSTFS_OBS_LOG_DIRECTORY`: + +- **Unset** — logs go to stdout. In containers, use your log driver; on systemd hosts, stdout/stderr is captured by **journald**, so `journalctl -u rustfs -f` works without extra configuration. +- **A local directory** (e.g. `/logs`) — RustFS writes rotating log files there. Rotation is tuned with `RUSTFS_OBS_LOG_FILENAME`, `RUSTFS_OBS_LOG_ROTATION_SIZE_MB`, `RUSTFS_OBS_LOG_ROTATION_TIME`, and `RUSTFS_OBS_LOG_KEEP_FILES`. +- **A URL** (contains `://`) — logs are shipped to a remote endpoint. + +When `RUSTFS_OBS_ENDPOINT` is set and log export is enabled, logs are additionally exported via OTLP; the reference stack routes them into Loki for querying from Grafana. + +```bash title="Example: file logs plus OTLP export" +RUSTFS_OBS_LOG_DIRECTORY=/var/log/rustfs +RUSTFS_OBS_LOGGER_LEVEL=info +RUSTFS_OBS_ENDPOINT=http://otel-collector:4318 +``` diff --git a/content/operations/rc.mdx b/content/operations/rc.mdx new file mode 100644 index 00000000..f248bcf4 --- /dev/null +++ b/content/operations/rc.mdx @@ -0,0 +1,170 @@ +--- +title: "rc" +description: "Install the RustFS command-line client and use it to connect to and inspect a RustFS cluster." +--- + +`rc` is the RustFS command-line client for S3 object operations and RustFS administration. This guide installs `rc`, configures a cluster alias, and introduces the main inspection commands. The download examples use `v0.1.30`; check the [RustFS CLI releases](https://github.com/rustfs/cli/releases) for a newer version before installing. + +## Installation + +### Linux + +The default Linux archives are statically linked with musl for broad compatibility. Choose the tab that matches `uname -m`. + + + + +For an `x86_64` host: + +```bash +curl -fLO https://github.com/rustfs/cli/releases/download/v0.1.30/rustfs-cli-linux-amd64-v0.1.30.tar.gz +tar -xzf rustfs-cli-linux-amd64-v0.1.30.tar.gz +sudo install -m 0755 rc /usr/local/bin/rc +``` + + + + +For an `aarch64` host: + +```bash +curl -fLO https://github.com/rustfs/cli/releases/download/v0.1.30/rustfs-cli-linux-arm64-v0.1.30.tar.gz +tar -xzf rustfs-cli-linux-arm64-v0.1.30.tar.gz +sudo install -m 0755 rc /usr/local/bin/rc +``` + + + + +If your environment specifically requires a glibc-linked binary, select the `linux-amd64-gnu` or `linux-arm64-gnu` archive from the release page. + +### macOS + +Choose the tab that matches the value returned by `uname -m`. + + + + +For an Intel (`x86_64`) Mac: + +```bash +curl -fLO https://github.com/rustfs/cli/releases/download/v0.1.30/rustfs-cli-macos-amd64-v0.1.30.tar.gz +tar -xzf rustfs-cli-macos-amd64-v0.1.30.tar.gz +sudo install -m 0755 rc /usr/local/bin/rc +``` + + + + +For an Apple silicon (`arm64`) Mac: + +```bash +curl -fLO https://github.com/rustfs/cli/releases/download/v0.1.30/rustfs-cli-macos-arm64-v0.1.30.tar.gz +tar -xzf rustfs-cli-macos-arm64-v0.1.30.tar.gz +sudo install -m 0755 rc /usr/local/bin/rc +``` + + + + +You can alternatively install the package through Homebrew: + +```bash +brew install rustfs/tap/rc +``` + +### Windows + +The current Windows release provides an AMD64 binary. Install it through the official Scoop bucket: + +```powershell +scoop bucket add rustfs https://github.com/rustfs/scoop-bucket +scoop install rustfs/rc +``` + +### Verify the installation + +Open a new terminal and verify that `rc` is available: + +```bash +rc --version +rc --help +``` + +## Usage + +### Configure an alias + +An alias stores the RustFS endpoint and credentials so you do not need to repeat them for every command. Replace the credentials before running: + +```bash +rc alias set local http://localhost:9000 +rc alias list +``` + +`alias set` replaces an existing alias with the same name. `alias list` does not print secret keys. + +### Check service health + +Check whether the service is live and whether its required dependencies are ready: + +```bash +rc ping local +rc ready local +``` + +### Inspect the cluster + +Use the administrative cluster information command to inspect cluster status, servers, disks, and storage capacity: + +```bash +rc admin info cluster local +``` + +List server pools when the deployment uses multiple pools: + +```bash +rc admin pool list local +``` + +Administrative commands require credentials with the corresponding RustFS Admin API permissions. + +### Inspect buckets and objects + +Prefer the noun-first command groups for new scripts: + +```bash +rc bucket list local/ +rc object list local/my-bucket +rc object stat local/my-bucket/hello.txt +``` + +### Manage lifecycle rules and storage tiers + +`rc` groups lifecycle rules, remote storage tiers, and restore requests under `bucket lifecycle`: + +```bash +rc bucket lifecycle rule list local/my-bucket +rc bucket lifecycle tier list local +rc bucket lifecycle restore local/my-bucket/hello.txt --days 7 +``` + +Use the `rule` commands to add, edit, remove, export, or import bucket lifecycle rules. Use the `tier` commands to add, inspect, update credentials for, or remove a remote tier. The older `rc ilm` form remains available as a compatibility alias, but we recommend `rc bucket lifecycle` for new scripts. + +See [Lifecycle Management](/administration/data/lifecycle-management) for rule and restore workflows, and [Tiered Storage](/administration/data/tiered-storage) for remote-tier configuration and maintenance. + +### Discover more commands + +Use `--help` at any command level to see the commands and options supported by your installed version: + +```bash +rc --help +rc alias --help +rc admin --help +rc admin info --help +rc bucket --help +rc bucket lifecycle --help +rc object --help +``` + +For the complete source and command reference, see the [RustFS CLI repository](https://github.com/rustfs/cli). diff --git a/content/operations/scaling/data-rebalancing.md b/content/operations/scaling/data-rebalancing.md new file mode 100644 index 00000000..5025f854 --- /dev/null +++ b/content/operations/scaling/data-rebalancing.md @@ -0,0 +1,84 @@ +--- +title: "Rebalancing" +description: "Redistribute existing objects across active RustFS storage pools through the Console or rc." +--- + +## Overview + +### Requirements + +- Install [`rc`](/operations/rc) on the administration host before using the `rc` workflow in this guide. +- Configure credentials with rebalance administration permission. + +After [Pool Expansion](./storage-pool-expansion.md), new writes can use the added capacity, but existing objects remain in their original pools. **Rebalancing** moves existing objects across all active pools so their used-capacity ratios converge. + +Rebalancing requires at least two active pools. RustFS rejects a new rebalance while another rebalance or a pool decommission is running. Stopping a rebalance does not move already relocated objects back to their original pools. + +Before starting, verify that all nodes and disks are healthy, no decommission is active, and the cluster has enough free capacity for normal writes during the operation. Schedule the operation during a lower-traffic period because object movement consumes disk, network, and CPU resources. + +## Operation + +### Start rebalancing in the Console + +1. Sign in to the RustFS Console with an account that has rebalance administration permission. +2. Open **Rebalance**. +3. Review the active pools and their used-capacity ratios. +4. Select **Start Rebalance** and confirm the operation. +5. Keep the page open or return to it periodically to review per-pool progress. +6. Use **Stop Rebalance** only when you need to halt the operation. Data already moved remains in its new pool. + +![RustFS Console showing a rebalance in progress with moved bytes and objects](./images/rebalance-running-console.png) + +### Start rebalancing with rc + +Configure the cluster alias if it does not already exist: + +```bash +rc alias set rustfs http://:9000 +``` + +Start the rebalance: + +```bash +rc admin rebalance start rustfs +``` + +Check progress: + +```bash +rc admin rebalance status rustfs +``` + +The status includes the operation ID and per-pool usage, moved bytes, object and version counts, remaining buckets, elapsed time, and estimated completion time when available. + +To stop a running rebalance: + +```bash +rc admin rebalance stop rustfs +``` + +`rc admin expand start|status|stop` and the `scale` alias expose the same post-expansion rebalance workflow. + +## Verification + +### Verify rebalancing in the Console + +Wait until the Rebalance page reports `Completed`. Confirm that: + +![RustFS Console showing a completed rebalance and balanced pool usage](./images/rebalance-completed-console.png) + +- No pool reports a failed or stopped state. +- Used-capacity ratios are closer across the active pools. +- All nodes and disks remain online. +- Normal object reads and writes succeed. + +### Verify rebalancing with rc + +Run: + +```bash +rc admin rebalance status rustfs +rc admin pool list rustfs +``` + +Verify that the rebalance status is `Completed`, remaining bucket counts are zero, and no failure is reported. Compare the per-pool usage ratios with the values recorded before the operation, then read an existing object and write a new test object through the S3 endpoint. diff --git a/content/operations/scaling/images/pool-decommission-completed-console.png b/content/operations/scaling/images/pool-decommission-completed-console.png new file mode 100644 index 00000000..1b37abc5 Binary files /dev/null and b/content/operations/scaling/images/pool-decommission-completed-console.png differ diff --git a/content/operations/scaling/images/pool-decommission-confirmation-console.png b/content/operations/scaling/images/pool-decommission-confirmation-console.png new file mode 100644 index 00000000..acf1b197 Binary files /dev/null and b/content/operations/scaling/images/pool-decommission-confirmation-console.png differ diff --git a/content/operations/scaling/images/pool-decommission-final-topology-console.png b/content/operations/scaling/images/pool-decommission-final-topology-console.png new file mode 100644 index 00000000..f5a93fab Binary files /dev/null and b/content/operations/scaling/images/pool-decommission-final-topology-console.png differ diff --git a/content/operations/scaling/images/pool-decommission-running-console.png b/content/operations/scaling/images/pool-decommission-running-console.png new file mode 100644 index 00000000..e80ffca6 Binary files /dev/null and b/content/operations/scaling/images/pool-decommission-running-console.png differ diff --git a/content/operations/scaling/images/pool-decommission-selection-console.png b/content/operations/scaling/images/pool-decommission-selection-console.png new file mode 100644 index 00000000..4e86f340 Binary files /dev/null and b/content/operations/scaling/images/pool-decommission-selection-console.png differ diff --git a/content/operations/scaling/images/pool-expansion-console.png b/content/operations/scaling/images/pool-expansion-console.png new file mode 100644 index 00000000..f5e7e9ac Binary files /dev/null and b/content/operations/scaling/images/pool-expansion-console.png differ diff --git a/content/operations/scaling/images/rebalance-completed-console.png b/content/operations/scaling/images/rebalance-completed-console.png new file mode 100644 index 00000000..17eb466a Binary files /dev/null and b/content/operations/scaling/images/rebalance-completed-console.png differ diff --git a/content/operations/scaling/images/rebalance-running-console.png b/content/operations/scaling/images/rebalance-running-console.png new file mode 100644 index 00000000..9e3ce4a4 Binary files /dev/null and b/content/operations/scaling/images/rebalance-running-console.png differ diff --git a/content/operations/scaling/index.md b/content/operations/scaling/index.md new file mode 100644 index 00000000..ffa65989 --- /dev/null +++ b/content/operations/scaling/index.md @@ -0,0 +1,10 @@ +--- +title: "Scaling" +description: "Expand, rebalance, and retire RustFS storage pools." +--- + +Choose the workflow that matches the topology change: + +- [Pool Expansion](./storage-pool-expansion.md) +- [Rebalancing](./data-rebalancing.md) +- [Pool Decommission](./storage-pool-decommission.md) \ No newline at end of file diff --git a/content/operations/scaling/meta.json b/content/operations/scaling/meta.json new file mode 100644 index 00000000..69f00562 --- /dev/null +++ b/content/operations/scaling/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Scaling", + "pages": [ + "storage-pool-expansion", + "data-rebalancing", + "storage-pool-decommission" + ] +} \ No newline at end of file diff --git a/content/operations/scaling/storage-pool-decommission.md b/content/operations/scaling/storage-pool-decommission.md new file mode 100644 index 00000000..4b881793 --- /dev/null +++ b/content/operations/scaling/storage-pool-decommission.md @@ -0,0 +1,119 @@ +--- +title: "Pool Decommission" +description: "Drain and retire a RustFS storage pool through the Console or rc, then verify that its data was relocated." +--- + +## Overview + +### Requirements + +- Install [`rc`](/operations/rc) on the administration host before using the `rc` workflow in this guide. +- Configure credentials with decommission administration permission. + +**Pool decommission** moves objects from a selected pool to the remaining active pools so the target pool can be removed from the deployment. Use it when retiring hardware, replacing a pool, or consolidating capacity. + +Decommission is different from [Rebalancing](./data-rebalancing.md): rebalance keeps every pool active, while decommission drains and retires the selected pool. The two operations cannot run at the same time. + +Before starting: + +- Keep at least one active pool after the operation. +- Verify that the remaining pools have enough free capacity. RustFS requires their free space to cover the used bytes being drained plus a 30% overhead. +- Confirm that every node and disk is healthy and no rebalance is running. +- Back up critical data and schedule the operation during a lower-traffic period. +- Record the exact pool ID and volume expression before selecting the target. + +Canceling a decommission does not roll back completed moves. Objects already relocated remain in the destination pools. + +## Operation + +### Start decommissioning in the Console + +1. Sign in to the RustFS Console with an account that has decommission administration permission. +2. Open **Pool Decommission**. +3. Locate the pool to retire and verify its ID, volume expression, used capacity, and status. + + ![RustFS Console showing the selected storage pool before decommissioning](./images/pool-decommission-selection-console.png) + +4. Select **Start Decommission** for that pool. +5. Review the confirmation dialog carefully, then select **Start Decommission**. + + ![RustFS Console showing the pool decommission confirmation dialog](./images/pool-decommission-confirmation-console.png) + +6. Use **Sync** to refresh the pool state and movement counters until the operation completes. + + ![RustFS Console showing pool decommission progress and movement counters](./images/pool-decommission-running-console.png) + +After completion, remove the drained pool expression from `RUSTFS_VOLUMES` on every remaining node and restart RustFS with the same ordered topology. For Helm, decommission the pool before removing its entry from `pools.list`; never remove or reorder a live pool entry. + +![RustFS Console showing the selected pool with a completed decommission status](./images/pool-decommission-completed-console.png) + +### Start decommissioning with rc + +Configure the cluster alias if needed, then list the pools: + +```bash +rc alias set rustfs http://:9000 +rc admin pool list rustfs +``` + +Start decommissioning pool `0` by its zero-based ID: + +```bash +rc admin decommission start rustfs 0 --by-id +``` + +You can instead pass the exact pool volume expression without `--by-id`: + +```bash +rc admin decommission start rustfs 'http://rustfs-node1:9000/data/rustfs{1...4}/mnmd' +``` + +Monitor all pools or only the target pool: + +```bash +rc admin decommission status rustfs +rc admin decommission status rustfs 0 --by-id +``` + +To cancel a running operation, use: + +```bash +rc admin decommission cancel rustfs 0 --by-id +``` + +If a decommission is failed or canceled, clear its metadata before retrying: + +```bash +rc admin decommission clear rustfs 0 --by-id +``` + +After the target reports `complete`, remove its expression from `RUSTFS_VOLUMES` on every remaining node and restart RustFS with the reduced topology. + +## Verification + +### Verify decommissioning in the Console + +Confirm that the target pool reports `Completed`, with zero failed objects and zero failed bytes. After removing the pool from the startup topology and restarting RustFS, verify that: + +![RustFS Console showing only the remaining pool after the retired pool was removed](./images/pool-decommission-final-topology-console.png) + +- The retired pool no longer appears as an active pool. +- Every remaining node and disk is online. +- Remaining pools show the relocated data and have adequate free capacity. +- Existing objects can still be listed, read, and downloaded. + +### Verify decommissioning with rc + +Before removing the pool from the topology, run: + +```bash +rc admin decommission status rustfs 0 --by-id +``` + +Confirm that the status is `complete` and the failed object and byte counters are zero. After updating `RUSTFS_VOLUMES` and restarting RustFS, run: + +```bash +rc admin pool list rustfs +``` + +Verify that only the intended active pools remain. Read objects that previously resided on the retired pool and write a new test object through the S3 endpoint. diff --git a/content/operations/scaling/storage-pool-expansion.md b/content/operations/scaling/storage-pool-expansion.md new file mode 100644 index 00000000..e5cb761e --- /dev/null +++ b/content/operations/scaling/storage-pool-expansion.md @@ -0,0 +1,104 @@ +--- +title: "Pool Expansion" +description: "Add a storage pool to a RustFS cluster and verify the expanded topology through the Console or rc." +--- + +## Overview + +### Requirements + +- Install [`rc`](/operations/rc) on the administration host before using the `rc` workflow in this guide. +- Configure credentials with permission to read RustFS cluster and pool status. + +RustFS expands capacity by appending a **server pool** to the cluster topology. Each pool is one space-separated volume expression in `RUSTFS_VOLUMES`. After the expanded topology is applied, new writes can use the added capacity; existing objects remain in their current pools until you run [Rebalancing](./data-rebalancing.md). + +This guide uses the following two-pool example: + +```ini title="/etc/default/rustfs" +RUSTFS_VOLUMES="http://rustfs-node1:9000/data/rustfs{1...4}/mnmd http://rustfs-node2:9000/data/rustfs{1...4}/mnmd" +``` + +Before expanding: + +- Back up critical data and run the workflow during a maintenance window. +- Use the same RustFS version, credentials, and complete `RUSTFS_VOLUMES` value on every node. +- Verify name resolution, time synchronization, and port `9000` connectivity between all old and new nodes. +- Prepare the new pool with the intended disk count and storage specification. + +:::warning[Append the pool; do not replace the topology] + +Every node must start with the complete ordered pool list. Omitting the existing pool or using a different expression on one node creates an inconsistent topology. + +::: + +## Operation + +### Expand with Console assistance + +The Console displays storage pools but does not add a pool to the server startup topology. Use this Console-assisted workflow: + +1. Record the existing pools and their usage from **Rebalance** or **Pool Decommission**. +2. Install the same RustFS version and service configuration on the new pool node or nodes. +3. Append the new pool expression to `RUSTFS_VOLUMES` on every existing and new node. Keep the existing expressions unchanged and in the same order. +4. Restart RustFS across all nodes so every process starts with the same expanded topology. +5. Wait for all nodes to become ready, then refresh the pool list in the Console. + +![RustFS Console showing the original and newly added storage pools](./images/pool-expansion-console.png) + +For a Helm deployment, append the new entry to `pools.list` and apply `helm upgrade`. Do not remove or reorder existing entries. + +### Inspect the expansion with rc + +Configure an alias with credentials that can read cluster and pool status: + +```bash +rc alias set rustfs http://:9000 +``` + +Record the current topology: + +```bash +rc admin pool list rustfs +``` + +Prepare the new nodes, append the new pool expression to the complete `RUSTFS_VOLUMES` value on every node, and restart RustFS across the cluster. `rc` does not mutate the server startup topology. + +After the cluster returns, list the pools again: + +```bash +rc admin pool list rustfs +``` + +Use a zero-based pool ID to inspect the new pool in detail: + +```bash +rc admin pool status rustfs 1 --by-id +``` + +:::note[Rebalance aliases] + +`rc admin expand start`, `status`, and `stop` are aliases for the post-expansion rebalance workflow. They redistribute existing data; they do not append a pool to `RUSTFS_VOLUMES`. + +::: + +## Verification + +### Verify the expansion in the Console + +Confirm that: + +- The pool list shows the original pool and the new pool. +- Every expected node and disk is online. +- The new pool reports the expected total and available capacity. +- The cluster reports no degraded nodes before you resume normal traffic. + +### Verify the expansion with rc + +Run: + +```bash +rc admin pool list rustfs +rc admin pool status rustfs 1 --by-id +``` + +Verify that the new pool has the expected command-line expression and an active state. Write and read a test object through the normal S3 endpoint before starting a rebalance. diff --git a/content/operations/status-check.md b/content/operations/status-check.md new file mode 100644 index 00000000..9627a3b5 --- /dev/null +++ b/content/operations/status-check.md @@ -0,0 +1,113 @@ +--- +title: "Status Check" +description: "Check RustFS network access, health endpoints, cluster status, and storage capacity." +--- + +Use network probes, the **Status** page in the RustFS Console, or the `rc` command line to review RustFS availability and storage consumption. Before using the command-line workflow, [install `rc`](/operations/rc) and configure an alias for the target cluster. + +## Network ports + +RustFS uses two listeners by default. You can change both bind addresses through environment variables or command-line flags. + +| Port | Listener | Configured by | Traffic | +| --- | --- | --- | --- | +| `9000` | S3 API | `RUSTFS_ADDRESS` / `--address` | S3 object API, admin API, and internal node-to-node RPC under `/rustfs/rpc/` and `/rustfs/peer/` | +| `9001` | Console | `RUSTFS_CONSOLE_ADDRESS` / `--console-address` | Embedded web console under `/rustfs/console/`; disable it with `RUSTFS_CONSOLE_ENABLE=false` | + +:::note[Internode connectivity] + +Internode RPC shares port 9000. In a multi-node cluster, every node must be able to reach every other node on the S3 port; there is no separate cluster port. + +::: + +Open the default ports with `firewalld` when clients need to reach both listeners: + +```bash title="firewall-cmd" +firewall-cmd --permanent --add-port=9000/tcp +firewall-cmd --permanent --add-port=9001/tcp +firewall-cmd --reload +``` + +:::warning[Restrict console access] + +Expose port 9001 only to trusted networks. If you do not need the console, set `RUSTFS_CONSOLE_ENABLE=false` and leave the port closed. + +::: + +## Health endpoints + +The S3 listener on port **9000** serves unauthenticated probe endpoints. Disable them with `RUSTFS_HEALTH_ENDPOINT_ENABLE=false`. The console on port **9001** has its own health path. + +| Endpoint | Port | Meaning | +| --- | --- | --- | +| `GET /health`, `HEAD /health`, `GET /health/live`, `GET /minio/health/live` | 9000 | Liveness — returns `200` as long as the process is up | +| `GET /health/ready` | 9000 | Readiness — `200` only when storage, IAM, and peer health are ready; `503` otherwise | +| `GET /minio/health/ready` | 9000 | MinIO-compatible alias of the readiness probe | +| `GET /minio/health/cluster`, `GET /minio/health/cluster/read` | 9000 | Cluster write/read health (additionally require lock quorum) | +| `GET /rustfs/console/health` | 9001 | Console process health | + +A ready node answers `200` with `"ready": true`. A degraded node answers `503` with per-dependency detail: + +```bash title="Probe examples" +curl -fsS http://localhost:9000/health +curl -s http://:9000/health/ready | jq +curl -fsS http://localhost:9001/rustfs/console/health +``` + +The `details` object reports `storage` / `iam` / `lock` (and `kms` when configured), and `degradedReasons` lists machine-readable causes such as `storage_quorum_unavailable`, `iam_not_ready`, or `lock_quorum_unavailable`. + +## Cluster status + +### Check cluster status in the Console + +1. Sign in to the RustFS Console. +2. Open **Status**. +3. Confirm that the cluster is online and review the server, network, and drive status. +4. Investigate any offline server, unavailable drive, or failed network connection before maintenance or capacity changes. + +### Check cluster status with rc + +Run the cluster information command with your configured alias: + +```bash +rc admin info cluster rustfs +``` + +The overview reports the cluster state, RustFS version, server and disk counts, backend type, and erasure-coding parity. The node list shows uptime, network connectivity, drive availability, and pool membership. The disk list shows each drive's state and pool, set, and disk location. + +For machine-readable output, request JSON: + +```bash +rc admin info cluster rustfs --json +``` + +## Storage capacity + +### Check storage capacity in the Console + +1. Sign in to the RustFS Console and open **Status**. +2. Review the cluster's used and total storage capacity. +3. Review the capacity and available space for each disk. +4. Compare usage over time and plan expansion before available capacity becomes insufficient for normal writes and maintenance. + +### Check storage capacity with rc + +Use the same cluster information command: + +```bash +rc admin info cluster rustfs +``` + +The **Storage** summary reports used capacity, total capacity, and the used percentage for the cluster. Each entry under **Disks** reports used, total, and available capacity for that drive. + +Use JSON output when collecting the values in a script or monitoring integration: + +```bash +rc admin info cluster rustfs --json +``` + +Administrative information commands require credentials with the corresponding RustFS Admin API permissions. + +## Next steps + +For continuous telemetry and alerting, continue with [Observability](./observability.md). To add storage, review [Pool Expansion](./scaling/storage-pool-expansion.md). diff --git a/content/operations/upgrade/binary/index.md b/content/operations/upgrade/binary/index.md new file mode 100644 index 00000000..594d2abe --- /dev/null +++ b/content/operations/upgrade/binary/index.md @@ -0,0 +1,50 @@ +--- +title: "Binary Upgrade" +description: "Upgrade RustFS binary deployments one node at a time while preserving cluster availability." +--- + +Use a rolling upgrade to replace the RustFS executable on each node without taking the entire cluster offline. Before you begin, read the target release notes and confirm every node is healthy. + +## Before you upgrade + +Check readiness on every node: + +```bash +curl -fsS http://:9000/health/ready +``` + +Back up the service configuration and retain the currently running executable for rollback: + +```bash +sudo cp /etc/default/rustfs /etc/default/rustfs.bak +sudo cp /usr/local/bin/rustfs /usr/local/bin/rustfs.previous +``` + +## Upgrade each node + +Upgrade one node at a time. Do not continue until the restarted node reports ready. + +```bash +sudo systemctl stop rustfs +sudo cp rustfs-new /usr/local/bin/rustfs +sudo chmod +x /usr/local/bin/rustfs +sudo systemctl start rustfs +curl -fsS http://:9000/health/ready +``` + +Repeat the procedure for each remaining node. + +## Roll back + +If the new version fails validation, restore the previous executable on one node at a time and wait for readiness before continuing: + +```bash +sudo systemctl stop rustfs +sudo cp /usr/local/bin/rustfs.previous /usr/local/bin/rustfs +sudo systemctl start rustfs +curl -fsS http://:9000/health/ready +``` + +## Next steps + +Review [Status Check](/operations/status-check) for additional post-upgrade validation. \ No newline at end of file diff --git a/content/operations/upgrade/container/index.md b/content/operations/upgrade/container/index.md new file mode 100644 index 00000000..071146bc --- /dev/null +++ b/content/operations/upgrade/container/index.md @@ -0,0 +1,157 @@ +--- +title: "Container Upgrade" +description: "Upgrade RustFS containers started with Docker, Podman, or Docker Compose while preserving persistent data." +--- + +Upgrade a container deployment by recreating the RustFS container from a newer image. Choose the workflow that matches the original installation method: a container started directly with Docker or Podman, or a service managed by Docker Compose. + +## Before you upgrade + +Read the target release notes and use an explicit image tag for the upgrade. Record the current image and retain the original `docker run`, `podman run`, or Compose configuration because recreating a container does not copy its runtime options automatically. + +Confirm that RustFS is healthy before replacing it: + +```bash +curl -fsS http://localhost:9000/health/ready +``` + +:::warning[Preserve persistent storage] + +Do not remove the named volume or host directory mounted at `/data`. Commands such as `docker compose down -v`, `docker volume rm`, and `podman volume rm` delete persistent storage and are not part of an upgrade. + +::: + +## Upgrade a Docker container + +The following workflow matches the container name and named volume used in the [Docker installation guide](/installation/container/docker). If your deployment uses different ports, environment variables, mounts, or startup arguments, keep those settings unchanged in the replacement command. + +Record the current image, then pull the target version: + +```bash +docker inspect --format '{{.Config.Image}}' rustfs +docker pull rustfs/rustfs: +``` + +Stop and remove only the container. The `rustfs-data` volume remains intact: + +```bash +docker stop rustfs +docker rm rustfs +``` + +Recreate the container with the original configuration and the target image: + +```bash +docker run -d \ + --name rustfs \ + --restart unless-stopped \ + -p 9000:9000 \ + -p 9001:9001 \ + -v rustfs-data:/data \ + -e RUSTFS_ACCESS_KEY="" \ + -e RUSTFS_SECRET_KEY="" \ + -e RUSTFS_CONSOLE_ENABLE=true \ + rustfs/rustfs: \ + /data +``` + +Wait for the replacement container to become healthy: + +```bash +docker ps --filter name=rustfs +docker logs rustfs +curl -fsS http://localhost:9000/health/ready +``` + +## Upgrade a Podman container + +The Podman workflow is the same replacement operation, using the image name from the [Podman installation guide](/installation/container/podman). + +```bash +podman inspect --format '{{.Config.Image}}' rustfs +podman pull docker.io/rustfs/rustfs: +podman stop rustfs +podman rm rustfs +``` + +Recreate the container with the original configuration and persistent volume: + +```bash +podman run -d \ + --name rustfs \ + -p 9000:9000 \ + -p 9001:9001 \ + -v rustfs-data:/data \ + -e RUSTFS_ACCESS_KEY="" \ + -e RUSTFS_SECRET_KEY="" \ + -e RUSTFS_CONSOLE_ENABLE=true \ + docker.io/rustfs/rustfs: \ + /data +``` + +Verify the replacement before continuing: + +```bash +podman ps --filter name=rustfs +podman logs rustfs +curl -fsS http://localhost:9000/health/ready +``` + +## Upgrade with Docker Compose + +Run Compose commands from the directory containing the deployment's `docker-compose.yml`. Before the upgrade, save the fully resolved configuration and confirm the current image: + +```bash +docker compose config > docker-compose.resolved.yaml +docker compose images rustfs +``` + +Change the `rustfs` service to an explicit target image tag while leaving its volumes, environment, ports, and command unchanged: + +```yaml title="docker-compose.yml" +services: + rustfs: + image: rustfs/rustfs: +``` + +Validate the file, pull the target image, and recreate only the RustFS service. `--no-deps` leaves optional observability services running: + +```bash +docker compose config --quiet +docker compose pull rustfs +docker compose up -d --no-deps rustfs +``` + +Check the service and the RustFS readiness endpoint: + +```bash +docker compose ps rustfs +docker compose logs --tail=100 rustfs +curl -fsS http://localhost:9000/health/ready +``` + +If you started RustFS together with the `observability` profile, the RustFS service still upgrades with the same commands. Upgrade observability images separately according to each component's release notes. + +## Upgrade a multi-node deployment + +Replace one RustFS node at a time. Wait for the upgraded node's readiness endpoint to succeed and confirm cluster status before proceeding to the next node. Do not recreate all nodes simultaneously. + +Use the same Docker, Podman, or Compose workflow on each node, preserving that node's existing mounts and configuration. + +## Roll back + +For Docker or Podman, stop and remove the failed replacement container, then repeat the corresponding `run` command with the previously recorded image tag. Keep the same persistent volume and runtime configuration. + +For Docker Compose, restore the previous `image` value in `docker-compose.yml`, then recreate the RustFS service: + +```bash +docker compose pull rustfs +docker compose up -d --no-deps rustfs +curl -fsS http://localhost:9000/health/ready +``` + +In a multi-node deployment, roll back one node at a time and wait for readiness before continuing. + +## Next steps + +Review [Status Check](/operations/status-check) for additional post-upgrade validation. \ No newline at end of file diff --git a/content/operations/upgrade/index.md b/content/operations/upgrade/index.md new file mode 100644 index 00000000..b9521a4a --- /dev/null +++ b/content/operations/upgrade/index.md @@ -0,0 +1,14 @@ +--- +title: "Upgrade" +description: "Plan and perform RustFS upgrades while preserving cluster availability." +--- + +Choose the upgrade workflow that matches how you deploy RustFS. For multi-node clusters, replace one node or pod at a time and wait for it to report ready before continuing. + +## Deployment methods + +- [Binary Upgrade](./binary/index.md) covers systemd-managed executable replacement and rollback. +- [Container Upgrade](./container/index.md) covers image replacement with persistent data volumes. +- [Kubernetes Upgrade](./kubernetes/index.md) covers Helm- and Operator-managed workloads. + +Before any upgrade, read the target release notes and confirm the cluster is healthy. \ No newline at end of file diff --git a/content/operations/upgrade/kubernetes/index.md b/content/operations/upgrade/kubernetes/index.md new file mode 100644 index 00000000..9914e146 --- /dev/null +++ b/content/operations/upgrade/kubernetes/index.md @@ -0,0 +1,198 @@ +--- +title: "Kubernetes Upgrade" +description: "Upgrade RustFS deployments managed by the Helm chart or RustFS Operator and verify each rollout." +--- + +Upgrade a Kubernetes deployment through the tool that owns its resources. Use Helm for a direct RustFS Helm chart installation. For an Operator installation, upgrade the Operator release and each Tenant through their respective declarative configurations. + +## Before you upgrade + +Read the RustFS, chart, and Operator release notes that apply to your deployment. Confirm that the cluster has enough capacity to replace pods and that all current workloads are ready: + +```bash +kubectl get nodes +kubectl -n get pods,pvc +``` + +Do not delete PersistentVolumeClaims (PVCs) during an upgrade or rollback. Keep the existing deployment values and manifests under version control, including any image tags, storage topology, scheduling rules, and Secret references. + +## Upgrade a Helm chart deployment + +This workflow applies to RustFS installed directly with the [RustFS Helm chart](/installation/cloud-native/helm-chart/installation). It does not apply to a Tenant created by RustFS Operator. + +Record the current release revision, chart version, application version, and effective user-supplied values: + +```bash +helm status rustfs --namespace rustfs +helm history rustfs --namespace rustfs +helm get values rustfs --namespace rustfs -o yaml > rustfs-values.previous.yaml +``` + +Prepare the target chart in the same way as the installation. If you use the chart repository, update its index and set the chart reference: + +```bash +helm repo update rustfs +export RUSTFS_CHART=rustfs/rustfs +``` + +If you use a source checkout, check out the target RustFS release and point `RUSTFS_CHART` at its chart directory: + +```bash +export RUSTFS_CHART=./helm/rustfs +``` + +Update your maintained values file for any release-specific changes. Set `image.rustfs.tag` to the explicit RustFS version you intend to run, then apply the upgrade: + +```bash +helm upgrade rustfs "$RUSTFS_CHART" \ + --namespace rustfs \ + -f \ + --set-string image.rustfs.tag= \ + --wait \ + --timeout 10m +``` + +Helm performs a rolling update. Monitor the release and pods until the rollout completes: + +```bash +helm status rustfs --namespace rustfs +kubectl -n rustfs get pods,pvc,services +``` + +Distributed mode creates a StatefulSet: + +```bash +kubectl -n rustfs rollout status statefulset/rustfs --timeout=10m +``` + +Standalone mode creates a Deployment: + +```bash +kubectl -n rustfs rollout status deployment/rustfs --timeout=10m +``` + +Verify the S3 API after the rollout. If it is not exposed outside the cluster, start the port forward documented in the installation guide first: + +```bash +curl -fsS http://localhost:9000/health/ready +``` + +### Roll back a Helm release + +List the release history, identify the last working revision, and let Helm restore its chart, values, and image configuration: + +```bash +helm history rustfs --namespace rustfs +helm rollback rustfs \ + --namespace rustfs \ + --wait \ + --timeout 10m +``` + +Check the workload and readiness endpoint again after rollback. Do not uninstall the release or delete its PVCs. + +## Upgrade an Operator deployment + +An Operator deployment has two independent upgrade surfaces: + +- The RustFS Operator and Operator Console run from the `rustfs-operator` Helm release. +- Each Tenant runs the RustFS image declared in that Tenant's `spec.image` field. + +Upgrade and validate the Operator control plane before changing Tenant images. Do not combine an Operator upgrade with multiple Tenant upgrades in one maintenance step. + +### Upgrade the Operator release + +Record the current release and save its values: + +```bash +helm status rustfs-operator --namespace rustfs-system +helm history rustfs-operator --namespace rustfs-system +helm get values rustfs-operator --namespace rustfs-system -o yaml \ + > rustfs-operator-values.previous.yaml +``` + +Update the Operator source checkout to the target release and review changes to its chart values and CustomResourceDefinitions (CRDs). Helm does not upgrade CRDs stored in a chart's `crds/` directory, so apply both target CRDs before upgrading the controller: + +```bash +kubectl apply --server-side --force-conflicts \ + --field-manager=rustfs-operator-crd-upgrade \ + -f deploy/rustfs-operator/crds/tenant.yaml +kubectl apply --server-side --force-conflicts \ + --field-manager=rustfs-operator-crd-upgrade \ + -f deploy/rustfs-operator/crds/policybinding-crd.yaml +``` + +The CRDs are cluster-scoped and shared by every Tenant namespace. Applying them first allows the Kubernetes API server to accept fields introduced by the new controller. After both commands succeed, upgrade the Operator with your maintained values file: + +```bash +helm upgrade rustfs-operator deploy/rustfs-operator/ \ + --namespace rustfs-system \ + -f \ + --set-string operator.image.tag= \ + --set-string console.image.tag= \ + --wait \ + --timeout 10m +``` + +Verify the CRDs and all control-plane components before upgrading a Tenant: + +```bash +kubectl get crd tenants.rustfs.com +kubectl -n rustfs-system rollout status deployment/rustfs-operator --timeout=10m +kubectl -n rustfs-system rollout status deployment/rustfs-operator-console --timeout=10m +kubectl get tenants --all-namespaces +``` + +CRD changes are not part of Helm release history and `helm rollback` does not restore them. Review the target release's compatibility and migration notes before considering a controller rollback. If that release explicitly supports downgrading, restore the previous Helm revision without applying older CRD files: + +```bash +helm history rustfs-operator --namespace rustfs-system +helm rollback rustfs-operator \ + --namespace rustfs-system \ + --wait \ + --timeout 10m +``` + +Do not downgrade across a documented one-way workload or security migration. Roll forward to a fixed Operator release instead. + +### Upgrade a Tenant + +The Operator owns each Tenant's StatefulSets. Do not change their container images directly because reconciliation restores the image declared by the Tenant. + +Record the current Tenant image and confirm its `Ready` condition: + +```bash +kubectl -n storage-a get tenant tenant-a \ + -o jsonpath='{.spec.image}{"\n"}' +kubectl -n storage-a get tenant tenant-a +kubectl -n storage-a get pods,pvc -l rustfs.tenant=tenant-a +``` + +In the complete `tenant.yaml` used to install the Tenant, change only `spec.image` to `rustfs/rustfs:`. Leave its pools, PVC templates, credentials, and other settings unchanged. Apply the complete manifest and wait for the Operator to reconcile the new generation: + +```bash +kubectl apply -f tenant.yaml +kubectl -n storage-a wait tenant/tenant-a \ + --for=condition=Ready \ + --timeout=10m +kubectl -n storage-a get pods -l rustfs.tenant=tenant-a +``` + +Upgrade one Tenant at a time. Confirm its S3 API and [cluster status](/operations/status-check) before updating another Tenant. + +### Roll back a Tenant + +Restore the previously recorded image in `spec.image`, apply the complete Tenant manifest, and wait for `Ready` again: + +```bash +kubectl apply -f tenant.yaml +kubectl -n storage-a wait tenant/tenant-a \ + --for=condition=Ready \ + --timeout=10m +``` + +The Operator reuses the Tenant's existing PVCs. Do not delete the Tenant, its StatefulSets, or its PVCs during rollback. + +## Next steps + +Review [Status Check](/operations/status-check) and [Observability](/operations/observability) to validate the deployment after the rollout. \ No newline at end of file diff --git a/content/operations/upgrade/meta.json b/content/operations/upgrade/meta.json new file mode 100644 index 00000000..e7d35086 --- /dev/null +++ b/content/operations/upgrade/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Upgrade", + "pages": [ + "[Binary](/operations/upgrade/binary)", + "[Container](/operations/upgrade/container)", + "[Kubernetes](/operations/upgrade/kubernetes)" + ] +} \ No newline at end of file diff --git a/content/reference/cli.md b/content/reference/cli.md index 1c75af56..ec5a537c 100644 --- a/content/reference/cli.md +++ b/content/reference/cli.md @@ -63,7 +63,9 @@ Every server flag has an environment variable equivalent; the flag wins when bot | `--buffer-profile-disable` | `RUSTFS_BUFFER_PROFILE_DISABLE` | `false` | Use legacy fixed-size buffers. | :::note + If neither `--access-key`/`--secret-key` nor their file variants are provided, the server falls back to the built-in default credentials (`rustfsadmin`/`rustfsadmin`) and logs a warning. Set real credentials for any non-throwaway deployment. + ::: ## Volume syntax diff --git a/content/reference/environment-variables.md b/content/reference/environment-variables.md index a55287bd..5933f780 100644 --- a/content/reference/environment-variables.md +++ b/content/reference/environment-variables.md @@ -5,8 +5,10 @@ description: "Reference for the environment variables that configure the RustFS This page lists the environment variables recognized by the `rustfs` server binary. It covers the operations-relevant subset verified against the RustFS source code; some internal tuning knobs are intentionally omitted. Every variable can also be supplied through the matching command-line flag where one exists (see the [CLI reference](./cli)). -:::note +:::note[Boolean values] + Boolean variables accept `true`/`false`. Values shown as "unset" have no default and the corresponding feature stays disabled or falls back to built-in behavior. + ::: ## Core server @@ -32,6 +34,15 @@ Boolean variables accept `true`/`false`. Values shown as "unset" have no default | `RUSTFS_CONSOLE_ENABLE` | `true` | Enable the embedded web console (served on a separate listener). | | `RUSTFS_CONSOLE_ADDRESS` | `:9001` | Bind address and port for the console listener. | +## CORS + +| Variable | Default | Description | +| --- | --- | --- | +| `RUSTFS_CORS_ALLOWED_ORIGINS` | empty | Comma-separated origins allowed to access the S3 API from a browser. Set `*` for wildcard origins without credentials. | +| `RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS` | `*` | Comma-separated origins allowed to access the Console from a browser, or `*` for all origins. | + +See [CORS Configuration](/administration/cors) for configuration and verification steps. + ## TLS & KMS | Variable | Default | Description | @@ -133,7 +144,9 @@ Metrics collection intervals follow the pattern `RUSTFS_METRICS__INTERVAL | `RUSTFS_RPC_SECRET` | derived | Secret used to authenticate internode RPC. When unset it is derived from the active access/secret key pair; with an all-default credential pair, multi-node clusters must set it explicitly. | :::warning + The built-in default credentials `rustfsadmin`/`rustfsadmin` are for first-boot convenience only. Always set non-default credentials for production deployments. + ::: ## Event & Audit targets diff --git a/content/reference/index.md b/content/reference/index.md new file mode 100644 index 00000000..8bcb95e5 --- /dev/null +++ b/content/reference/index.md @@ -0,0 +1,11 @@ +--- +title: "Reference" +description: "Look up RustFS configuration variables and command-line options." +--- + +Use this section for concise technical references while configuring, operating, or integrating RustFS. + +## References + +- [Environment Variables](./environment-variables.md) +- [CLI](./cli.md) \ No newline at end of file diff --git a/content/reference/meta.json b/content/reference/meta.json new file mode 100644 index 00000000..c82645dd --- /dev/null +++ b/content/reference/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Reference", + "defaultOpen": false, + "pages": [ + "environment-variables", + "cli" + ] +} \ No newline at end of file diff --git a/content/reference/ports.md b/content/reference/ports.md deleted file mode 100644 index 7ecda68b..00000000 --- a/content/reference/ports.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: "Ports and Health Endpoints" -description: "Reference for the network ports RustFS listens on, the health check endpoints it exposes, and a minimal firewall configuration example." ---- - -RustFS uses two listeners. Both bind addresses are configurable; the values below are the defaults. - -## Port matrix - -| Port | Listener | Configured by | Traffic | -| --- | --- | --- | --- | -| `9000` | S3 API | `RUSTFS_ADDRESS` / `--address` | S3 object API, admin API, and internal node-to-node RPC (paths under `/rustfs/rpc/` and `/rustfs/peer/`) — all on the same port. | -| `9001` | Console | `RUSTFS_CONSOLE_ADDRESS` / `--console-address` | Embedded web console (UI served under `/rustfs/console/`). Enabled by default; disable with `RUSTFS_CONSOLE_ENABLE=false`. | - -:::note -Because internode RPC shares port 9000, every node in a multi-node cluster must be able to reach every other node on the S3 port. There is no separate cluster port to open. -::: - -## Health endpoints - -Health probes are served unauthenticated on the S3 listener (port 9000) and can be disabled with `RUSTFS_HEALTH_ENDPOINT_ENABLE=false`: - -| Endpoint | Purpose | -| --- | --- | -| `GET /health` | Liveness probe (also accepts `HEAD`). `/health/live` is an alias. | -| `GET /health/ready` | Readiness probe; reports whether storage is initialized and ready to serve. | -| `GET /minio/health/live`, `/minio/health/ready`, `/minio/health/cluster`, `/minio/health/cluster/read` | MinIO-compatible probe aliases for existing tooling. | - -The console listener (port 9001) exposes its own probe at `GET /rustfs/console/health`. - -```bash title="Probe examples" -curl -fsS http://localhost:9000/health -curl -fsS http://localhost:9000/health/ready -curl -fsS http://localhost:9001/rustfs/console/health -``` - -## Firewall example - -Open the S3 API and console ports with `firewalld`: - -```bash title="firewall-cmd" -firewall-cmd --permanent --add-port=9000/tcp -firewall-cmd --permanent --add-port=9001/tcp -firewall-cmd --reload -``` - -:::warning -Expose port 9001 (console) only to trusted networks. If the console is not needed, set `RUSTFS_CONSOLE_ENABLE=false` and do not open the port. -::: diff --git a/content/security-compliance/audit-logs.md b/content/security-compliance/audit-logs.md new file mode 100644 index 00000000..593410ac --- /dev/null +++ b/content/security-compliance/audit-logs.md @@ -0,0 +1,143 @@ +--- +title: "Audit Targets" +description: "Configure RustFS audit targets and deliver request-level audit records to external systems." +--- + +RustFS Audit Targets send request-level activity records to external systems for security monitoring, compliance, and incident investigation. This guide configures a webhook target and verifies audit delivery with an S3 request. + +## Overview + +Audit Targets and Event Notifications serve different purposes: + +| Feature | Audit Targets | Event Notifications | +| --- | --- | --- | +| Scope | Request and API activity across RustFS | Events selected by each bucket configuration | +| Routing | Every enabled audit target receives audit records | Each bucket rule selects a target | +| Filtering | No per-target event filter | Event, prefix, and suffix filters | +| Typical use | Security information and event management (SIEM), compliance, investigation | Application workflows and data pipelines | + +RustFS supports webhook, Kafka, MQTT, MySQL, PostgreSQL, NATS, Redis, AMQP, and Pulsar audit target families. Each enabled target receives the audit stream independently. + +Audit records can contain request paths, query parameters, selected headers, identity claims, access-key identifiers, remote hosts, user agents, response status, and errors. Protect the destination and restrict access according to your retention and compliance requirements. + +## Configuration + +Audit delivery is disabled by default. The following environment variables enable the module and create a webhook target named `primary`: + +```bash +export RUSTFS_AUDIT_ENABLE="true" +export RUSTFS_AUDIT_WEBHOOK_ENABLE_PRIMARY="on" +export RUSTFS_AUDIT_WEBHOOK_ENDPOINT_PRIMARY="https://audit.example.com/rustfs" +export RUSTFS_AUDIT_WEBHOOK_AUTH_TOKEN_PRIMARY="" +export RUSTFS_AUDIT_WEBHOOK_QUEUE_DIR_PRIMARY="/var/lib/rustfs/audit-primary" +export RUSTFS_OUTBOUND_ALLOW_ORIGINS="https://audit.example.com" +``` + +Restart RustFS after changing environment-managed targets. The target name is the lowercase form of the environment-variable suffix, so `_PRIMARY` creates `primary`. + +RustFS sends a `HEAD` health check to the endpoint origin's root path, such as `https://audit.example.com/`, and sends JSON `POST` requests to the complete configured endpoint. The origin must respond to `HEAD`, while delivery requests must return a successful HTTP status. Allow the RustFS service account to write to the queue directory. + +### Webhook settings + +Use the same `_PRIMARY` suffix on all settings for this target: + +| Variable | Purpose | +| --- | --- | +| `RUSTFS_AUDIT_WEBHOOK_ENDPOINT_PRIMARY` | HTTP or HTTPS delivery endpoint | +| `RUSTFS_AUDIT_WEBHOOK_AUTH_TOKEN_PRIMARY` | Bearer token sent in the `Authorization` header | +| `RUSTFS_AUDIT_WEBHOOK_QUEUE_DIR_PRIMARY` | Directory for queued audit records | +| `RUSTFS_AUDIT_WEBHOOK_QUEUE_LIMIT_PRIMARY` | Maximum number of queued records | +| `RUSTFS_AUDIT_WEBHOOK_CLIENT_CERT_PRIMARY` | Client certificate for mutual TLS (mTLS) | +| `RUSTFS_AUDIT_WEBHOOK_CLIENT_KEY_PRIMARY` | Private key paired with the client certificate | +| `RUSTFS_AUDIT_WEBHOOK_CLIENT_CA_PRIMARY` | Certificate authority used to verify the receiver | +| `RUSTFS_AUDIT_WEBHOOK_SKIP_TLS_VERIFY_PRIMARY` | Disable server certificate verification | + +:::warning[Protect audit data] + +Use HTTPS for remote collectors. Keep certificate verification enabled, store bearer tokens outside source control, and provide both the client certificate and key when using mTLS. Audit payloads may contain security-sensitive request and identity metadata. + +::: + +For all target families and target-specific keys, see [Environment Variables](/reference/environment-variables#event--audit-targets). + +### Multiple targets + +Add a distinct uppercase suffix to define another instance. For example, use `RUSTFS_AUDIT_WEBHOOK_ENABLE_ARCHIVE` and the corresponding `_ARCHIVE` settings to create an `archive` webhook alongside `primary`. + +RustFS fans each audit record out to all enabled targets. A failure in one target does not prevent delivery attempts to the others. + +:::note[Environment-managed targets] + +A target defined by environment variables cannot be edited or removed through the Console or admin API. Change its environment variables and restart RustFS instead. Do not define the same target through both environment variables and persisted configuration. + +::: + +## Usage + +:::note[rc command-line support] + +The current `rc` client does not provide commands for creating, listing, or removing Audit Targets. Configure them through environment variables or the Console. The `rc bucket event` commands manage bucket notification rules only; they do not manage audit delivery. + +::: + +### Verify the receiver + +Before generating traffic, verify that the collector is reachable from the RustFS host and accepts the configured bearer token: + +```bash +curl --fail --head \ + https://audit.example.com/ +``` + +The health check measures origin reachability and does not include the webhook authorization token. Separately confirm that the configured `/rustfs` delivery path accepts `Authorization: Bearer ` on `POST` requests. + +If the target remains offline, check DNS, firewall policy, `RUSTFS_OUTBOUND_ALLOW_ORIGINS`, certificate trust, and the origin's response to `HEAD /`. + +### Generate an audit record + +Perform an authenticated S3 operation against RustFS: + +```bash +printf 'hello from RustFS\n' > hello.txt + +aws --endpoint-url http://localhost:9000 \ + --region us-east-1 \ + s3 cp hello.txt s3://my-bucket/hello.txt +``` + +The webhook receives a JSON audit entry. Fields vary by operation and empty optional fields may be omitted. A representative subset is: + +```json +{ + "version": "1", + "time": 1719331200000, + "event": "s3:ObjectCreated:Put", + "trigger": "s3", + "api": { + "name": "PutObject", + "bucket": "my-bucket", + "object": "hello.txt", + "status": "OK", + "statusCode": 200 + }, + "remotehost": "192.0.2.10", + "requestID": "", + "userAgent": "aws-cli", + "requestPath": "/my-bucket/hello.txt", + "accessKey": "" +} +``` + +Treat the payload schema as additive. Parse the fields you need and tolerate unknown or absent fields so collectors remain compatible with later RustFS releases. + +### Delivery behavior + +When `RUSTFS_AUDIT_WEBHOOK_QUEUE_DIR_PRIMARY` is configured, RustFS stores pending records on disk and replays them after transient delivery failures. Size the queue directory and `RUSTFS_AUDIT_WEBHOOK_QUEUE_LIMIT_PRIMARY` for the expected outage window, then monitor both disk usage and target health. + +Webhook delivery is at least once. A timeout can occur after the receiver processes a request but before RustFS observes the response, so use `requestID` and other stable fields to deduplicate records where required. + +The receiver should acknowledge a record only after it has durably accepted it. Returning a non-success status keeps failures visible to the delivery path; silently accepting and discarding a record cannot be recovered by RustFS. + +## Next steps + +Configure [Event Notifications](/operations/event-notifications) for bucket-scoped application workflows. Review [Observability](/operations/observability) to monitor service health and [Credentials](/operations/credentials) to limit administrative access. \ No newline at end of file diff --git a/content/security-compliance/encryption/index.md b/content/security-compliance/encryption/index.md new file mode 100644 index 00000000..0737b6cf --- /dev/null +++ b/content/security-compliance/encryption/index.md @@ -0,0 +1,14 @@ +--- +title: "Data Encryption" +description: "Find the verified RustFS server-side encryption and key management references." +--- + +RustFS provides server-side encryption options for data stored in the object storage service. Use this section to choose the key-management model that matches your security requirements. + +## Encryption options + +- [SSE-S3](./sse-s.md) covers server-managed server-side encryption with the S3 `AES256` mode. +- [SSE-C](./sse-c.md) covers server-side encryption with customer-provided keys. +- [SSE-KMS](./kms.md) covers Local, Vault KV2, and Vault Transit key-management backends. + +For the currently verified KMS configuration surface, see the [CLI reference](/reference/cli) and [environment variable reference](/reference/environment-variables). \ No newline at end of file diff --git a/content/security-compliance/encryption/kms.md b/content/security-compliance/encryption/kms.md new file mode 100644 index 00000000..23b3315f --- /dev/null +++ b/content/security-compliance/encryption/kms.md @@ -0,0 +1,157 @@ +--- +title: "SSE-KMS" +description: "Configure SSE-KMS with a local or HashiCorp Vault KMS backend for RustFS." +--- + +RustFS Key Management Service (KMS) generates and wraps per-object data encryption keys for [SSE-S3](./sse-s.md) and SSE-KMS. This guide configures SSE-KMS at server startup with a local key store, Vault KV v2 plus Transit, or Vault Transit. + +## Requirements + +- Install and configure [`rc`](/operations/rc) for the encryption verification workflow. +- Back up all existing data and KMS key material before changing encryption configuration. +- Apply the same KMS backend and default key ID to every RustFS node. +- Use a process manager or secret manager that injects secrets without committing them to source control. +- For Vault, enable the required secrets engines, create a least-privilege token, and make Vault reachable from every RustFS node. +- Provision a KMS master key before configuring its ID as the RustFS default key. + +:::warning[Key loss makes encrypted objects unreadable] + +RustFS does not store a recoverable copy of your KMS master key outside the configured backend. Back up local key files and their master key, or protect Vault data and recovery credentials, before writing encrypted objects. + +::: + +## Choose a backend + +| Backend | `RUSTFS_KMS_BACKEND` | Key storage and wrapping | Intended use | +| --- | --- | --- | --- | +| Local | `local` | Key files on the RustFS host | Development, testing, or a carefully backed-up single-host deployment | +| Vault KV2 | `vault` or `vault-kv2` | Metadata in Vault KV v2; wrapping through Vault Transit | Centralized production key management | +| Vault Transit | `vault-transit` | Cryptographic operations through Vault Transit | Centralized production key management without the KV2 backend mode | + +SSE-S3 and SSE-KMS both require the KMS service to be running. Configuring a bucket default alone does not make encrypted writes succeed when KMS is unavailable. + +## Configure the local backend + +Create an absolute key directory owned by the RustFS service account: + +```bash +sudo install -d -m 0700 -o rustfs -g rustfs /var/lib/rustfs/kms +``` + +Add the KMS settings to the RustFS environment file on every node: + +```ini title="/etc/default/rustfs" +RUSTFS_KMS_ENABLE=true +RUSTFS_KMS_BACKEND=local +RUSTFS_KMS_KEY_DIR=/var/lib/rustfs/kms +RUSTFS_KMS_LOCAL_MASTER_KEY= +``` + +`RUSTFS_KMS_KEY_DIR` is required by the RustFS server startup path. The local backend writes key files with owner-only permissions. RustFS rejects development defaults such as a temporary key directory or missing local master key unless you explicitly set `RUSTFS_KMS_ALLOW_INSECURE_DEV_DEFAULTS=true`; never enable that override in production. + +Restart RustFS after updating the environment. This starts the KMS backend but does not create a master key: + +```bash +sudo systemctl restart rustfs +sudo systemctl status rustfs --no-pager +``` + +## Configure Vault KV2 + +Enable a KV v2 engine for key metadata and a Transit engine for key wrapping. Then configure every RustFS node: + +```ini title="/etc/default/rustfs" +RUSTFS_KMS_ENABLE=true +RUSTFS_KMS_BACKEND=vault-kv2 +RUSTFS_KMS_VAULT_ADDRESS=https://vault.example.com:8200 +RUSTFS_KMS_VAULT_TOKEN= +RUSTFS_KMS_VAULT_MOUNT_PATH=transit +``` + +The server startup interface uses `secret` as the KV mount and `rustfs/kms/keys` as the key prefix. Use a Vault token that can read and write that KV path and perform the required Transit operations. + +RustFS validates the Vault URL and rejects insecure development defaults unless `RUSTFS_KMS_ALLOW_INSECURE_DEV_DEFAULTS=true` is set. Use HTTPS and a certificate trusted by the RustFS hosts in production. + +## Configure Vault Transit + +Enable the Transit engine and configure every RustFS node: + +```ini title="/etc/default/rustfs" +RUSTFS_KMS_ENABLE=true +RUSTFS_KMS_BACKEND=vault-transit +RUSTFS_KMS_VAULT_ADDRESS=https://vault.example.com:8200 +RUSTFS_KMS_VAULT_TOKEN= +RUSTFS_KMS_VAULT_MOUNT_PATH=transit +``` + +Vault Transit retains historical key versions, so rotating a Transit key does not by itself make objects wrapped with an older version unreadable. Keep old versions enabled for as long as objects depend on them. + +## Create and select the default key + +`RUSTFS_KMS_DEFAULT_KEY_ID` selects an existing key; it does not create one. Start the KMS backend without this variable, create the key, and only then add the variable and restart RustFS. + +When your installed `rc` exposes the KMS lifecycle commands, create and inspect a key with: + +```bash +rc admin kms status rustfs +rc admin kms key create rustfs --name rustfs-default-key +rc admin kms key status rustfs rustfs-default-key +``` + +Then add the same default key ID on every RustFS node: + +```ini title="/etc/default/rustfs" +RUSTFS_KMS_DEFAULT_KEY_ID=rustfs-default-key +``` + +Restart every node consistently after changing the default. + +For Vault Transit, you can alternatively create the named Transit key directly in Vault before configuring it as the default: + +```bash +vault write -f transit/keys/rustfs-default-key +``` + +:::note[rc version boundary] + +The tested `rc 0.1.29` on the validation host does not expose `rc admin kms`; it supports only the bucket and object encryption commands used below. Run `rc admin --help` before following the key lifecycle commands. If the KMS command family is absent, use a newer `rc` release that includes it or provision the key through the native RustFS KMS Admin API. + +::: + +## Verify KMS-backed encryption + +Create a test bucket, configure SSE-KMS with the default key, upload an object, and read it back: + +```bash +rc bucket create rustfs/my-bucket +rc bucket encryption set rustfs/my-bucket --mode sse-kms +rc bucket encryption info rustfs/my-bucket +rc object copy /path/to/hello.txt rustfs/my-bucket/hello.txt +rc object show rustfs/my-bucket/hello.txt +``` + +To select a specific KMS key, set it on the bucket default or the individual write: + +```bash +rc bucket encryption set rustfs/my-bucket \ + --mode sse-kms \ + --key-id rustfs-default-key + +rc object copy /path/to/hello.txt rustfs/my-bucket/hello.txt \ + --enc-kms rustfs/my-bucket/hello.txt=rustfs-default-key +``` + +The selector before `=` must exactly match the destination passed to `rc object copy`. + +## Rotate and recover keys + +- **Vault token:** issue a replacement token, update every RustFS node, restart consistently, verify encrypted reads and writes, and then revoke the old token. +- **Vault Transit key:** rotate the Transit key in Vault. New wrapping operations use the new version while Vault retains older versions for decryption. +- **Local or Vault KV2 key material:** back up the current material before rotation and verify that objects written before and after rotation remain readable. +- **Existing objects:** rotating a master key does not rewrite object data. Each object keeps the wrapped data key stored with its encryption metadata. + +Do not delete or disable an old master-key version until you have established that no retained object depends on it. + +## Next steps + +Configure [SSE-S3](./sse-s.md), or use [SSE-C](./sse-c.md) when the client must retain control of the encryption key. \ No newline at end of file diff --git a/content/security-compliance/encryption/meta.json b/content/security-compliance/encryption/meta.json new file mode 100644 index 00000000..a288b978 --- /dev/null +++ b/content/security-compliance/encryption/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Data Encryption", + "pages": [ + "sse-s", + "sse-c", + "kms" + ] +} \ No newline at end of file diff --git a/content/security-compliance/encryption/sse-c.md b/content/security-compliance/encryption/sse-c.md new file mode 100644 index 00000000..f397d1b6 --- /dev/null +++ b/content/security-compliance/encryption/sse-c.md @@ -0,0 +1,84 @@ +--- +title: "SSE-C" +description: "Use customer-provided AES-256 keys for RustFS object encryption and decryption." +--- + +SSE-C encrypts an object with a 256-bit key supplied by the client on every write and read request. RustFS validates and uses the key for the request but does not persist the plaintext customer key. SSE-C does not use the RustFS KMS backend. + +## Requirements + +- Install and configure [`rc`](/operations/rc). +- Install OpenSSL for the key-generation example. +- Use HTTPS so customer keys are encrypted in transit. +- Store the customer key in a secret manager and maintain a mapping between each object and its key. + +:::warning[The same key is required for every read] + +If you lose an SSE-C key, RustFS cannot recover it and the object becomes unreadable. Never log, commit, or send the key over an unencrypted connection. + +::: + +## Prepare a customer key + +Generate 32 random bytes through a hexadecimal intermediate, then derive the Base64 key and its Base64-encoded MD5 checksum: + +```bash +SSE_C_KEY_HEX=$(openssl rand -hex 32) +SSE_C_KEY_B64=$(printf '%s' "$SSE_C_KEY_HEX" | xxd -r -p | openssl base64 -A) +SSE_C_KEY_MD5=$(printf '%s' "$SSE_C_KEY_HEX" | xxd -r -p \ + | openssl dgst -md5 -binary | openssl base64 -A) +``` + +Move these values into your approved secret-management workflow. Do not print them to the terminal or shell history. + +## Upload an encrypted object + +`rc 0.1.29` has no dedicated SSE-C option. Its signed custom-header option can send the three S3 SSE-C headers: + +```bash +rc object copy /path/to/hello.txt rustfs/my-bucket/hello.txt \ + -H "x-amz-server-side-encryption-customer-algorithm:AES256" \ + -H "x-amz-server-side-encryption-customer-key:$SSE_C_KEY_B64" \ + -H "x-amz-server-side-encryption-customer-key-md5:$SSE_C_KEY_MD5" +``` + +The required request headers are: + +| Header | Value | +| --- | --- | +| `x-amz-server-side-encryption-customer-algorithm` | `AES256` | +| `x-amz-server-side-encryption-customer-key` | Base64-encoded 32-byte key | +| `x-amz-server-side-encryption-customer-key-md5` | Base64-encoded MD5 digest of the raw key | + +## Read the encrypted object + +Supply the same three headers on reads and metadata requests: + +```bash +rc object show rustfs/my-bucket/hello.txt \ + -H "x-amz-server-side-encryption-customer-algorithm:AES256" \ + -H "x-amz-server-side-encryption-customer-key:$SSE_C_KEY_B64" \ + -H "x-amz-server-side-encryption-customer-key-md5:$SSE_C_KEY_MD5" +``` + +The same workflow was validated with `rc 0.1.29` against RustFS by uploading an object, reading it with the original key, and comparing the returned bytes with the source. + +:::warning[Protect command arguments] + +The generic `-H` method places expanded header values in the `rc` process arguments, which may be visible to other processes on the host. For production automation, prefer an S3 SDK that accepts SSE-C key material through protected memory or a credential provider instead of command-line arguments. + +::: + +## Clear shell variables + +After the operation, remove key material from the shell environment: + +```bash +unset SSE_C_KEY_HEX SSE_C_KEY_B64 SSE_C_KEY_MD5 +``` + +Changing a bucket's SSE-S3 or SSE-KMS default does not alter SSE-C objects. SSE-C is selected by the customer-key headers on each individual request and takes precedence for that request. + +## Next steps + +Compare [SSE-S3](./sse-s.md) for server-managed encryption and [KMS](./kms.md) for explicit KMS-backed key management. \ No newline at end of file diff --git a/content/security-compliance/encryption/sse-s.md b/content/security-compliance/encryption/sse-s.md new file mode 100644 index 00000000..7a4a9929 --- /dev/null +++ b/content/security-compliance/encryption/sse-s.md @@ -0,0 +1,77 @@ +--- +title: "SSE-S3" +description: "Configure server-managed SSE-S3 encryption for RustFS buckets and object writes." +--- + +SSE-S3 uses the S3 `AES256` encryption mode. RustFS generates a unique data encryption key for each encrypted object and uses the configured KMS service to wrap that key. Clients do not provide or retain the encryption key. + +## Requirements + +- Configure and start [KMS](./kms.md), create its default key, and verify the key is available before enabling SSE-S3. +- Install and configure [`rc`](/operations/rc). +- Create the target bucket and verify that normal object writes succeed. + +:::warning[KMS availability is required] + +RustFS accepts an SSE-S3 bucket default even when KMS is unavailable, but encrypted object writes then fail. Verify an encrypted write and read before using the bucket for production data. + +::: + +## Set bucket default encryption + +Apply SSE-S3 to new writes that do not specify an object-level encryption mode: + +```bash +rc bucket encryption set rustfs/my-bucket --mode sse-s3 +rc bucket encryption info rustfs/my-bucket +``` + +The reported mode should be `SSE-S3`. + +Upload and read a test object: + +```bash +rc object copy /path/to/hello.txt rustfs/my-bucket/hello.txt +rc object show rustfs/my-bucket/hello.txt +``` + +## Encrypt one object explicitly + +Apply SSE-S3 to a single destination without changing the bucket default: + +```bash +rc object copy /path/to/hello.txt rustfs/my-bucket/hello.txt \ + --enc-s3 rustfs/my-bucket/hello.txt +``` + +For recursive writes, the encryption selector must exactly match the destination prefix: + +```bash +rc object copy ./reports/ rustfs/my-bucket/reports/ --recursive \ + --enc-s3 rustfs/my-bucket/reports/ +``` + +## Clear the bucket default + +Remove the default rule: + +```bash +rc bucket encryption clear rustfs/my-bucket +rc bucket encryption info rustfs/my-bucket +``` + +Clearing or changing the bucket default affects only later writes. It does not decrypt or rewrite existing objects. + +## Request contract + +S3 clients request SSE-S3 with: + +```http +x-amz-server-side-encryption: AES256 +``` + +An object-level request overrides the bucket default for that write. If no object-level mode is supplied, RustFS resolves the effective mode from the bucket encryption configuration. + +## Next steps + +Use [SSE-KMS](./kms.md#verify-kms-backed-encryption) when you need an explicit KMS key ID, or compare [SSE-C](./sse-c.md) for client-held keys. \ No newline at end of file diff --git a/content/administration/iam/access-token.md b/content/security-compliance/iam/access-token.md similarity index 100% rename from content/administration/iam/access-token.md rename to content/security-compliance/iam/access-token.md diff --git a/content/administration/iam/images/access_token_creation.png b/content/security-compliance/iam/images/access_token_creation.png similarity index 100% rename from content/administration/iam/images/access_token_creation.png rename to content/security-compliance/iam/images/access_token_creation.png diff --git a/content/administration/iam/images/access_token_deletion.png b/content/security-compliance/iam/images/access_token_deletion.png similarity index 100% rename from content/administration/iam/images/access_token_deletion.png rename to content/security-compliance/iam/images/access_token_deletion.png diff --git a/content/administration/iam/index.md b/content/security-compliance/iam/index.md similarity index 97% rename from content/administration/iam/index.md rename to content/security-compliance/iam/index.md index e375079f..53b0ccf3 100644 --- a/content/administration/iam/index.md +++ b/content/security-compliance/iam/index.md @@ -51,5 +51,4 @@ For service accounts and STS sessions carrying a session policy, the effective p - [Users, Groups, and Policies](./policies.md) — managing users and groups, the policy document format, and built-in policies. - [Service Accounts and STS](./sts.md) — derived access keys and temporary credentials via `AssumeRole`. -- [External Identity (OIDC)](./oidc.md) — Console SSO with Keycloak, Authing, or any standard OpenID Connect provider. - [Access Keys](./access-token.md) — creating and deleting access keys from the Console. diff --git a/content/security-compliance/iam/meta.json b/content/security-compliance/iam/meta.json new file mode 100644 index 00000000..451db04e --- /dev/null +++ b/content/security-compliance/iam/meta.json @@ -0,0 +1,8 @@ +{ + "title": "IAM", + "pages": [ + "[Users, Groups, and Policies](/security-compliance/iam/policies)", + "[Service Accounts and STS](/security-compliance/iam/sts)", + "[Access Keys](/security-compliance/iam/access-token)" + ] +} diff --git a/content/administration/iam/policies.md b/content/security-compliance/iam/policies.md similarity index 94% rename from content/administration/iam/policies.md rename to content/security-compliance/iam/policies.md index b6c024a6..a0827f64 100644 --- a/content/administration/iam/policies.md +++ b/content/security-compliance/iam/policies.md @@ -5,9 +5,14 @@ description: "Managing RustFS IAM users and groups, the policy document JSON for This page covers day-to-day IAM administration: creating users and groups, attaching policies, and writing custom policy documents. +## Requirements + +- Install [`rc`](/operations/rc) before using RustFS administrative commands. +- Configure an alias with credentials that permit the corresponding `admin:*` actions. + ## Managing Users -Users can be managed from the Console (**Identity** section in the left navigation) or through the admin REST API. All admin endpoints live under the `/rustfs/admin/v3` prefix; a MinIO-compatible prefix (`/minio/admin`) is also served for `mc admin` / madmin-style clients. Requests must be signed (AWS Signature V4) by a credential whose policies allow the corresponding `admin:*` action. +Users can be managed from the Console (**Identity** section in the left navigation), with `rc admin`, or through the admin REST API. The native admin endpoints live under the `/rustfs/admin/v3` prefix. Requests must be signed (AWS Signature V4) by a credential whose policies allow the corresponding `admin:*` action. | Operation | Method and path | Notes | | --- | --- | --- | diff --git a/content/administration/iam/sts.md b/content/security-compliance/iam/sts.md similarity index 98% rename from content/administration/iam/sts.md rename to content/security-compliance/iam/sts.md index c88d88b3..8f9d2c09 100644 --- a/content/administration/iam/sts.md +++ b/content/security-compliance/iam/sts.md @@ -53,7 +53,7 @@ Omit `accessKey`/`secretKey` to have RustFS generate a random pair. Omit `policy RustFS implements an AWS-compatible STS endpoint at the server root (`POST /` with form-encoded parameters). Two actions are supported: - `AssumeRole` — for existing IAM identities, signed with SigV4. -- `AssumeRoleWithWebIdentity` — for OIDC identities, authenticated by the JWT itself (see [External Identity (OIDC)](./oidc.md)). +- `AssumeRoleWithWebIdentity` — for OIDC identities, authenticated by the JWT itself (see [External Identity (OIDC)](../oidc/index.md)). ### AssumeRole diff --git a/content/security-compliance/index.md b/content/security-compliance/index.md new file mode 100644 index 00000000..68960b85 --- /dev/null +++ b/content/security-compliance/index.md @@ -0,0 +1,15 @@ +--- +title: "Security & Compliance" +description: "Configure identity, encryption, transport security, audit logging, and credentials for RustFS." +--- + +Use this section to secure access to RustFS and configure the controls required by your environment. + +## Security areas + +- [IAM Management](./iam/index.md) covers users, groups, policies, and access keys. +- [Data Encryption](./encryption/index.md) covers server-side encryption options and key management. +- [OIDC](./oidc/index.md) covers external identity providers. +- [TLS Configuration](/integration/tls-configured) covers encrypted client and node connections. +- [Audit Logs](./audit-logs.md) covers audit target configuration. +- [Credential Management](/operations/credentials) covers root credentials and secret injection. diff --git a/content/security-compliance/meta.json b/content/security-compliance/meta.json new file mode 100644 index 00000000..e2c7ad17 --- /dev/null +++ b/content/security-compliance/meta.json @@ -0,0 +1,12 @@ +{ + "title": "Security & Compliance", + "defaultOpen": true, + "pages": [ + "iam", + "encryption", + "oidc", + "[TLS Configuration](/integration/tls-configured)", + "audit-logs", + "[Credential Management](/operations/credentials)" + ] +} diff --git a/content/security-compliance/oidc/index.md b/content/security-compliance/oidc/index.md new file mode 100644 index 00000000..676df9e9 --- /dev/null +++ b/content/security-compliance/oidc/index.md @@ -0,0 +1,39 @@ +--- +title: "OIDC" +description: "Understand how RustFS uses OpenID Connect providers for Console single sign-on." +--- + +RustFS supports OpenID Connect (OIDC) for Console single sign-on. Use this overview to understand the authentication flow, prepare an identity provider, and choose a provider-specific configuration guide. + +## How OIDC works + +RustFS uses the OIDC Authorization Code Flow with Proof Key for Code Exchange (PKCE): + +1. RustFS sends an authorization request with an S256 PKCE challenge. +2. The identity provider authenticates you and returns an authorization code to the RustFS callback URL. +3. RustFS exchanges the code at the provider's token endpoint. +4. RustFS verifies the ID token signature, issuer, audience, expiry, and nonce. +5. RustFS maps configured token claims to local Identity and Access Management (IAM) policies and creates a Console session with temporary credentials. + +The identity provider authenticates you, while RustFS policies authorize S3 and administration operations. + +## Provider requirements + +Before configuring a provider, prepare: + +- An OIDC discovery URL that exposes the authorization, token, and JSON Web Key Set (JWKS) endpoints. +- A confidential client that supports the Authorization Code Flow, S256 PKCE, and client-secret authentication in the token request body. +- An exact callback URL in the form `https:///rustfs/admin/v3/oidc/callback/`. +- A public HTTPS origin for the RustFS Console. +- A flat group or role claim whose values match existing RustFS policy names. + +Use a dedicated client for RustFS and grant each identity only the policies it requires. + +## Provider guides + +- [Configure Keycloak](/security-compliance/oidc/keycloak) as an OIDC provider for the RustFS Console. + +## Next steps + +- Review [users, groups, and policies](/security-compliance/iam/policies) before mapping identity-provider claims. +- Review the [Console security notes](/administration/console) before exposing the login endpoint publicly. diff --git a/content/security-compliance/oidc/keycloak.md b/content/security-compliance/oidc/keycloak.md new file mode 100644 index 00000000..a761ce44 --- /dev/null +++ b/content/security-compliance/oidc/keycloak.md @@ -0,0 +1,173 @@ +--- +title: "Keycloak" +description: "Configure Keycloak as an OpenID Connect identity provider for RustFS Console single sign-on." +--- + +RustFS integrates with **Keycloak** through the OpenID Connect (OIDC) Authorization Code Flow. The examples on this page use the default RustFS provider ID, `default`. + +## Overview + +The browser login flow is: + +1. RustFS sends an authorization-code request with a Proof Key for Code Exchange (PKCE) S256 challenge. +2. Keycloak authenticates the user and redirects the browser to RustFS with `code` and `state`. +3. RustFS exchanges the code at the Keycloak token endpoint. +4. RustFS verifies the ID token signature, issuer, audience, expiry, and nonce. +5. RustFS maps ID token claim values to local RustFS Identity and Access Management (IAM) policies and issues temporary credentials for the Console session. + +Keycloak authenticates the user, while RustFS policies authorize S3 and administration operations. RustFS does not use Keycloak Authorization Services for object or Console authorization. + +The examples use the following values. Replace the hostnames and client secret for your environment: + +| Setting | Example | +| --- | --- | +| Keycloak realm | `rustfs` | +| Keycloak issuer | `https://keycloak.example.com/realms/rustfs` | +| Keycloak client ID | `rustfs-console` | +| Public RustFS origin | `https://rustfs.example.com` | +| RustFS callback URL | `https://rustfs.example.com/rustfs/admin/v3/oidc/callback/default` | + +## Configuration + +### Keycloak configuration + +#### Create the realm and client + +1. Open the Keycloak Admin Console and create or select the `rustfs` realm. +2. Open **Clients** and create an OpenID Connect client with the client ID `rustfs-console`. +3. Enable **Client authentication** and **Standard flow**. +4. Disable unused flows, including **Implicit flow**, **Direct access grants**, and **Service accounts roles**. +5. Set **Valid redirect URIs** to the exact RustFS callback URL: + + ```text + https://rustfs.example.com/rustfs/admin/v3/oidc/callback/default + ``` + +6. Set **Web origins** to the public RustFS origin: + + ```text + https://rustfs.example.com + ``` + +7. Set **Proof Key for Code Exchange Code Challenge Method** to `S256`. +8. Save the client and copy its secret from **Credentials**. + +RustFS submits the client secret in the token request body. Do not apply a Keycloak client policy that disables `client_secret_post`. + +#### Map Keycloak groups to RustFS policies + +RustFS maps values in the ID token `groups` claim to RustFS policy names. Create Keycloak groups that match the policies required by your users, such as `consoleAdmin`, `readwrite`, or `readonly`, then assign users to those groups. + +Add a **Group Membership** mapper to a dedicated client scope and attach it to `rustfs-console`: + +| Mapper field | Value | +| --- | --- | +| Name | `rustfs-groups` | +| Token Claim Name | `groups` | +| Full group path | Off | +| Add to ID token | On | +| Add to access token | On | +| Add to userinfo | On | +| Multivalued | On | + +Keep **Full group path** disabled. A value such as `/consoleAdmin` does not match the RustFS policy named `consoleAdmin`. + +:::note[Using Keycloak roles] + +If you use realm or client roles instead of groups, add a role mapper that emits a flat top-level `roles` array. RustFS does not parse Keycloak's nested `realm_access.roles` claim. + +::: + +### RustFS configuration + +Configure the Keycloak provider through environment variables. + +#### Configure with environment variables + +Add the Keycloak provider and public browser origin to the RustFS service environment: + +```ini title="/etc/default/rustfs" +RUSTFS_BROWSER_REDIRECT_URL="https://rustfs.example.com" + +RUSTFS_IDENTITY_OPENID_ENABLE=on +RUSTFS_IDENTITY_OPENID_CONFIG_URL="https://keycloak.example.com/realms/rustfs" +RUSTFS_IDENTITY_OPENID_CLIENT_ID="rustfs-console" +RUSTFS_IDENTITY_OPENID_CLIENT_SECRET="" +RUSTFS_IDENTITY_OPENID_SCOPES="openid,profile,email" +RUSTFS_IDENTITY_OPENID_REDIRECT_URI="https://rustfs.example.com/rustfs/admin/v3/oidc/callback/default" +RUSTFS_IDENTITY_OPENID_REDIRECT_URI_DYNAMIC=off +RUSTFS_IDENTITY_OPENID_DISPLAY_NAME="Keycloak" +RUSTFS_IDENTITY_OPENID_GROUPS_CLAIM="groups" +RUSTFS_IDENTITY_OPENID_ROLES_CLAIM="roles" +RUSTFS_IDENTITY_OPENID_EMAIL_CLAIM="email" +RUSTFS_IDENTITY_OPENID_USERNAME_CLAIM="preferred_username" +``` + +Restart RustFS after applying the configuration. + +`RUSTFS_BROWSER_REDIRECT_URL` must contain the public scheme and authority without a path. It controls the Console success redirect and logout fallback URL. The provider callback URL must exactly match the URL registered in Keycloak. + +:::warning[Map claims in production] + +Do not use `RUSTFS_IDENTITY_OPENID_ROLE_POLICY=consoleAdmin` as a permanent shortcut. Map Keycloak groups or roles to RustFS policies so each user receives only the required permissions. + +::: + +For a reverse proxy or load balancer, preserve the callback query string and route the authorize and callback requests to the same RustFS node. The in-flight OIDC `state` is local to that node. + +## Verification + +### Verify Keycloak discovery + +Query the realm discovery document: + +```bash +curl -fsS "https://keycloak.example.com/realms/rustfs/.well-known/openid-configuration" | jq '{ + issuer, + authorization_endpoint, + token_endpoint, + jwks_uri, + code_challenge_methods_supported, + token_endpoint_auth_methods_supported +}' +``` + +Confirm that: + +- `issuer` is `https://keycloak.example.com/realms/rustfs`. +- `authorization_endpoint`, `token_endpoint`, and `jwks_uri` are present. +- `code_challenge_methods_supported` includes `S256`. +- The token endpoint supports client-secret authentication in the request body. + +### Verify the RustFS provider + +After restarting RustFS, check that the provider is available: + +```bash +curl -fsS "https://rustfs.example.com/rustfs/admin/v3/oidc/providers" | jq +``` + +The response should include the `default` provider with the display name `Keycloak`. + +### Test Console login + +Open the RustFS Console and select **Keycloak**, or open the authorization endpoint directly: + +```text +https://rustfs.example.com/rustfs/admin/v3/oidc/authorize/default +``` + +Verify the complete flow: + +1. The browser redirects to Keycloak. +2. The user signs in. +3. Keycloak redirects to the RustFS callback URL with `code` and `state`. +4. RustFS validates the ID token and creates the Console session. +5. The Console opens with the permissions mapped from the user's `groups` or `roles` claim. + +If authentication succeeds but access is denied, confirm that the ID token contains a flat `groups` or `roles` claim and that each value exactly matches an existing RustFS policy name. + +## Next steps + +- Review [users, groups, and policies](../iam/policies.md) before assigning Keycloak groups. +- Review the [Console security notes](/administration/console) before exposing the login endpoint publicly. diff --git a/content/security-compliance/oidc/meta.json b/content/security-compliance/oidc/meta.json new file mode 100644 index 00000000..853c25c4 --- /dev/null +++ b/content/security-compliance/oidc/meta.json @@ -0,0 +1,4 @@ +{ + "title": "OIDC", + "pages": ["keycloak"] +} \ No newline at end of file diff --git a/content/troubleshooting/healing.md b/content/troubleshooting/healing.md deleted file mode 100644 index 00eccb76..00000000 --- a/content/troubleshooting/healing.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: "Object Inspection and Auto-Recovery" -description: "How RustFS object self-healing works: design principles, trigger paths, scrub and repair process, and usage notes." ---- - -## RustFS Architecture and Self-Healing Design - -### Erasure-Coded Storage Pools - -RustFS organizes disks — on a single node or across nodes — into erasure-set storage pools. Each object is split into data shards and parity shards when written, and distributed across different disks (and nodes) to improve reliability and performance. - -### Self-Healing Design Principles - -1. **Data Integrity Verification**: Combines checksum mechanisms to ensure object shard data consistency during reads, for example, ZFS validates each data block's checksum during reads and repairs when validation fails. -2. **Shard Redundancy and Erasure Coding**: Generates redundant shards through erasure coding. When some data shards are lost or corrupted, original objects can be reconstructed using redundant shards. -3. **Multi-Level Self-Healing Triggers**: Includes online self-healing during reads, background scanning self-healing, and manual trigger self-healing to balance performance and data reliability. - -## Object Self-Healing Principles - -### Verification and Erasure Coding - -During object write phase, RustFS splits objects into *k* data shards and *m* redundant shards, distributed across *n=k+m* block devices according to specified erasure parameters. During reads, if shard corruption or loss is detected, reconstruction can be performed from other intact shards. - -### Data Verification and Repair (Scrub & Repair) - -RustFS periodically performs lightweight verification (light scrub) and deep verification (deep scrub) on storage pools: -- **Lightweight Verification**: Compares object metadata and shard sizes, marking corruption when discovered. -- **Deep Verification**: Reads shard data bit by bit and verifies checksums, detecting and repairing hidden bad blocks or bit rot issues. - -When data scanning discovers inconsistencies, RustFS automatically calls the Repair process, reconstructing corrupted shards using redundant shards, and writes repaired shards back to original disks or backup disks, ensuring data integrity for next access. - -## Self-Healing Process - -### Online Self-Healing During Reads - -When a client executes a `GET` or `HEAD` request, RustFS reads the data shards required to serve the object (a read quorum, not every shard): -1. If enough shards are intact, data is returned directly. -2. If shards are lost or corrupted, the system reconstructs the missing shards from parity shards, repairs them, then returns the complete object to the client. -This enables transparent data repair without affecting client requests. - -### Background Scanning Self-Healing - -RustFS has a built-in object scanner that traverses 1/1024 of objects in the storage pool using hash methods for integrity checks: -- Object scanner runs lightweight verification periodically (configurable frequency); -- If corruption is discovered, self-healing reconstruction process is immediately triggered. -Deep bit-rot verification runs on its own cycle (30 days by default) and can be tuned or disabled to trade thoroughness against resource overhead. - -### Manual Trigger Self-Healing - -Administrators can trigger a full heal through the RustFS Console or the admin API. A full heal scans the entire storage pool and performs complete verification and repair on all objects, consuming significant resources, so it should be used cautiously during low-peak periods. - -## Summary - -RustFS's object self-healing combines mature designs from systems like MinIO, Ceph, and ZFS. Through multi-level triggered verification and repair processes, it can effectively handle shard corruption, disk failures, and bit rot issues in both single-machine multi-disk and multi-machine multi-disk environments, ensuring high reliability and high availability of object storage. diff --git a/content/troubleshooting/index.md b/content/troubleshooting/index.md index 0ae092c0..2d67143d 100644 --- a/content/troubleshooting/index.md +++ b/content/troubleshooting/index.md @@ -7,5 +7,5 @@ Contents: - [Node Failures](./node.md) - [Driver Failures](./driver.md) -- [Object Inspection and Auto-Recovery](./healing.md) +- [Node Healing](../operations/high-availability/node-healing.md) diff --git a/content/troubleshooting/meta.json b/content/troubleshooting/meta.json new file mode 100644 index 00000000..df5e0579 --- /dev/null +++ b/content/troubleshooting/meta.json @@ -0,0 +1,8 @@ +{ + "title": "Troubleshooting", + "defaultOpen": false, + "pages": [ + "driver", + "node" + ] +} \ No newline at end of file diff --git a/content/upgrade-scale/availability-and-resiliency.md b/content/upgrade-scale/availability-and-resiliency.md deleted file mode 100644 index efeee9b8..00000000 --- a/content/upgrade-scale/availability-and-resiliency.md +++ /dev/null @@ -1,266 +0,0 @@ ---- -title: "Availability and Scalability Description" -description: "This article will detail the technology and description related to RustFS scaling." ---- - -> Note: This document is based on the latest RustFS version. Please perform full data backup before scaling operations. For production environments, it's recommended to contact RustFS technical support engineers for solution review. - -## Scaling Solution Overview - -RustFS supports horizontal scaling by adding new storage pools (Server Pool). Each new storage pool must meet: - -1. Nodes within the storage pool must use **consecutive hostnames** (e.g., node5-node8) -2. Single storage pool must use **same specifications** of disks (type/capacity/quantity) -3. New storage pools must maintain **time synchronization** and **network connectivity** with existing clusters - -```mermaid -flowchart LR - APP[Applications] --> S3API(["S3 API"]) - - subgraph DIST["Distributed RustFS"] - direction TB - subgraph N1["Node 1"] - direction LR - S3a[S3] - subgraph OL1["Object Layer"] - direction TB - C1[Cache] - K1[Compression] - E1[Encryption] - B1["Erasure Code · Bitrot"] - end - SL1["Storage Layer"] - J1[("JBOD / FS disks")] - S3a -->|Object API| OL1 - OL1 -->|Storage API| SL1 - SL1 <--> J1 - end - subgraph N2["Node 2"] - direction LR - S3b[S3] - subgraph OL2["Object Layer"] - direction TB - C2[Cache] - K2[Compression] - E2[Encryption] - B2["Erasure Code · Bitrot"] - end - SL2["Storage Layer"] - J2[("JBOD / FS disks")] - S3b -->|Object API| OL2 - OL2 -->|Storage API| SL2 - SL2 <--> J2 - end - NN["Node n ..."] - N1 <-->|Internal RESTful API| N2 - N2 <-->|Internal RESTful API| NN - end - - S3API --> N1 - S3API --> N2 - S3API --> NN - - classDef server fill:#dbeafe,stroke:#3b82f6,stroke-width:2px,color:#1e293b; - classDef store fill:#dcfce7,stroke:#22c55e,stroke-width:2px,color:#1e293b; - classDef svc fill:#eef2ff,stroke:#6366f1,stroke-width:2px,color:#1e293b; - classDef muted fill:#f3f4f6,stroke:#9ca3af,stroke-width:2px,color:#1e293b; - classDef accent fill:#fae8ff,stroke:#c026d3,stroke-width:2px,color:#1e293b; - class APP,NN muted - class S3API accent - class S3a,S3b,SL1,SL2 server - class C1,K1,E1,B1,C2,K2,E2,B2 svc - class J1,J2 store -``` - ---- - -## Pre-Scaling Preparation - -### 1.1 Hardware Planning Requirements - -| Item | Minimum Requirements | Recommended Production Configuration | -|---------------|---------------------------|---------------------------| -| Node Count | 4 nodes/storage pool | 4 - 8 nodes/storage pool | -| Single Node Memory | 128 GB | 128 GB | -| Disk Type | SSD | NVMe SSD | -| Single Disk Capacity | ≥1 TB | ≥4 TB | -| Network Bandwidth | 10 Gbps | 25 Gbps | - -### 1.2 System Environment Check - -```bash -# Check hostname continuity (new node example) -cat /etc/hosts -192.168.10.5 node5 -192.168.10.6 node6 -192.168.10.7 node7 -192.168.10.8 node8 - -# Verify time synchronization status -timedatectl status | grep synchronized - -# Check firewall rules (all nodes need to open ports 9000/9001) -firewall-cmd --list-ports | grep 9000 -firewall-cmd --list-ports | grep 9001 -``` - ---- - -## Scaling Implementation Steps - -### 2.1 New Node Basic Configuration - -```bash -# Create dedicated user (execute on all new nodes) -groupadd rustfs-user -useradd -M -r -g rustfs-user rustfs-user - -# Create storage directories (example with 8 disks) -mkdir -p /data/rustfs{0..7} -chown -R rustfs-user:rustfs-user /data/rustfs* -``` - -### 2.2 Install RustFS Binary on all new nodes - -```bash -# Check rustfs version on existing node -/usr/local/bin/rustfs --version - -# Download the binary that matches the existing cluster version from -# https://github.com/rustfs/rustfs/releases (asset name: rustfs-linux-x86_64-musl-v.zip) -wget https://github.com/rustfs/rustfs/releases/download//rustfs-linux-x86_64-musl-v.zip -unzip rustfs-linux-x86_64-musl-v.zip -chmod +x rustfs -mv rustfs /usr/local/bin/ -``` - -### 2.3 Create RustFS configuration file on all new nodes (/etc/default/rustfs) - -```bash -# Create configuration file (/etc/default/rustfs) -# Please replace and with yours values! -cat < /etc/default/rustfs -RUSTFS_ACCESS_KEY= # e.g. admin -RUSTFS_SECRET_KEY= # e.g. output of: openssl rand -base64 24 -RUSTFS_VOLUMES="http://node{1...4}:9000/data/rustfs{0...3} http://node{5...8}:9000/data/rustfs{0...7}" # add new storage pool to the existing; must match the hostname pattern used by the existing nodes byte for byte -RUSTFS_ADDRESS=":9000" -RUSTFS_CONSOLE_ADDRESS=":9001" -EOF -``` - -### 2.4 Configure System Service on all new nodes - -```bash -# Create systemd service file - -sudo tee /etc/systemd/system/rustfs.service <> /etc/sysctl.conf - echo "net.core.somaxconn=32768" >> /etc/sysctl.conf - sysctl -p - ``` - ---- - -## Troubleshooting Guide - -| Symptom | Check Point | Fix Command | -|---------------------------|---------------------------------|-------------------------------| -| New nodes cannot join cluster | Check port 9000 connectivity | `telnet node5 9000` | -| Uneven data distribution | Check storage pool capacity configuration | Start a rebalance from the RustFS Console | -| Console shows abnormal node status | Verify time synchronization status | `chronyc sources` | diff --git a/content/upgrade-scale/index.md b/content/upgrade-scale/index.md deleted file mode 100644 index b74515a1..00000000 --- a/content/upgrade-scale/index.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: "Availability and Scalability" -description: "Learn about RustFS upgrades and scaling." ---- - -Contents: - -- [Availability and Resiliency Description](./availability-and-resiliency.md) -- Upgrade -- Scaling -- Retirement -- Rebalancing diff --git a/content/upgrade-scale/upgrade.md b/content/upgrade-scale/upgrade.md deleted file mode 100644 index a1a2ddd9..00000000 --- a/content/upgrade-scale/upgrade.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -title: "Upgrade" -description: "Zero-downtime rolling upgrade runbook for multi-node RustFS clusters: pre-checks, node-by-node procedure, degraded-startup semantics, rollback, and verification." ---- - -This runbook describes how to upgrade the RustFS binary (or container image) on a multi-node, erasure-coded cluster without losing availability, and how to read the degraded-mode signals if several nodes end up down at once. - -:::note[Upgrades never change the on-disk format] - -Upgrading the binary or container image never changes the on-disk data format. Replacing the executable and restarting is safe; no migration step runs on startup. - -::: - -## When a rolling restart applies - -A **rolling restart** — restarting one node at a time while the rest keep serving — covers: - -- Binary or container image upgrades -- Changes to environment variables or startup parameters on individual nodes (edits to `/etc/default/rustfs`) - -It does **not** cover topology changes. Changing `RUSTFS_VOLUMES` — for example, adding a Server Pool — must be applied to **every** node's configuration, and all nodes must be restarted so the whole cluster agrees on the new layout; expect a short interruption while the cluster converges. See [Availability and Scalability](./availability-and-resiliency.md) for the pool expansion workflow. - -## Why one node at a time - -Erasure coding shards every object — including internal metadata such as IAM users, groups, and policies under `.rustfs.sys` — across the drives of a set. Reading data back needs a read quorum of shards online. While one node is down, the rest of the cluster keeps quorum and serves all traffic. If you take a second node down before the first is back, some erasure sets may lose write or even read quorum and requests start failing — this is the situation to avoid. - -## Pre-checks - -Before touching the first node: - -1. **Confirm the cluster is fully healthy.** Every node should return `200`: - - ```bash - curl -fsS http://:9000/health/ready - ``` - - Do not start a rolling upgrade on a cluster that already has offline nodes or degraded erasure sets. - -2. **Back up each node's configuration** (generic precaution — it holds credentials and the volume layout): - - ```bash - sudo cp /etc/default/rustfs /etc/default/rustfs.bak-$(date +%F) - ``` - -3. **Keep the currently running binary** so rollback is a file copy, not a download: - - ```bash - sudo cp /usr/local/bin/rustfs /usr/local/bin/rustfs.previous - ``` - -4. **Read the release notes** of the target version for any version-specific guidance. - -## Rolling upgrade procedure - -For each node, in any order, **one at a time**: - -### 1. Replace the binary - -```bash -# stage the new binary, then swap it in -sudo systemctl stop rustfs -sudo cp rustfs-new /usr/local/bin/rustfs -sudo chmod +x /usr/local/bin/rustfs -``` - -(For container deployments, update the image tag on this node instead.) - -### 2. Restart the node - -```bash -sudo systemctl start rustfs -``` - -The bundled systemd unit uses `Type=notify` with `TimeoutStartSec=120s`: systemd waits for the server's readiness notification, and the timeout is sized to cover initialization plus readiness checks on slower disks or cold starts. Do not shorten it. - -### 3. Wait until the node reports ready - -```bash -curl -fsS http://:9000/health/ready -``` - -A ready node returns `200` with `"ready": true` in the JSON body. Only then move on to the next node. - -### 4. Repeat for the remaining nodes - -Same steps, next node — never two in parallel. - -## If a node comes up degraded - -Nodes started before the cluster has quorum (relevant when several nodes are down at once, e.g. after a power loss) come up in **degraded mode** — the process stays alive and recovers automatically: - -- S3 requests receive `503 Service Unavailable` with a `Retry-After: 5` header, an `x-rustfs-readiness-pending` header, and a body naming the blocking dependency: `storage_quorum` (waiting for the erasure read quorum), `iam` (storage is up, IAM cache still loading), or `startup_finalization` (last startup steps being published). -- The IAM recovery loop retries with backoff and logs `event="iam_bootstrap_retry_failed"` with an actionable `hint` field. After repeated failures the log level escalates from WARN to ERROR — this still does not kill the process. -- As soon as enough peers are online, pending nodes finish IAM bootstrap on the next retry and flip `/health/ready` to `200` on their own. - -:::warning[Do not restart-loop degraded nodes] - -Recovery is automatic; restarting a degraded node does not speed anything up. Just keep starting the remaining nodes and wait. - -::: - -While waiting, `/health/ready` returns per-dependency detail: - -```bash -curl -s http://:9000/health/ready | jq -``` - -The `details` object shows `storage` / `iam` / `lock` readiness, and `degradedReasons` lists machine-readable causes such as `storage_quorum_unavailable` or `lock_quorum_unavailable`. - -`RUSTFS_STARTUP_READINESS_MAX_WAIT_SECS` (default `120`) controls how long startup waits for full readiness before continuing in degraded mode with background recovery; recovery retries continue regardless of this limit. - -## Rollback - -Because no migration runs on startup, rollback is the same rolling procedure in reverse — one node at a time, with the previous binary: - -```bash -sudo systemctl stop rustfs -sudo cp /usr/local/bin/rustfs.previous /usr/local/bin/rustfs -sudo systemctl start rustfs -curl -fsS http://:9000/health/ready # wait for 200 before the next node -``` - -If you also changed `/etc/default/rustfs`, restore the backup taken during pre-checks before restarting. - -## Verification - -After the last node is back: - -1. **All nodes ready:** `curl -fsS http://:9000/health/ready` returns `200` on every node. -2. **Console check:** open the Console (`http://:9001`), confirm every server is listed online and shows the new version. -3. **Functional smoke test** with any S3 client, e.g. `mc`: - - ```bash - mc alias set rustfs http://:9000 - mc mb rustfs/upgrade-smoke-test - mc cp ./somefile rustfs/upgrade-smoke-test/ - mc cat rustfs/upgrade-smoke-test/somefile > /dev/null && echo OK - mc rb --force rustfs/upgrade-smoke-test - ``` - -## What is not normal - -- A node process **exiting** with a fatal IAM/lock error during startup — that fatal path was removed after `v1.0.0-beta.5`; upgrade if you still see it. -- A node stuck degraded **after** the whole cluster is back: check network reachability between nodes (peer RPC ports) and per-node clocks, then inspect `degradedReasons` and the `hint` field of the IAM retry logs. - -## Related - -- [Availability and Scalability](./availability-and-resiliency.md) — adding Server Pools -- [Multiple Node Multiple Disk](../installation/linux/multiple-node-multiple-disk.md) — cluster deployment layout -- [Node troubleshooting](../troubleshooting/node.md) — diagnosing offline nodes diff --git a/package-lock.json b/package-lock.json index d08d3bad..a4bab488 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "mermaid": "^11.16.0", "react": "^19.2.7", "react-dom": "^19.2.7", + "react-icons": "^5.7.0", "react-server-dom-webpack": "^19.2.7", "rehype-katex": "^7.0.1", "remark-math": "^6.0.0", @@ -6210,6 +6211,15 @@ "react": "^19.2.7" } }, + "node_modules/react-icons": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.7.0.tgz", + "integrity": "sha512-LBLy340Rzqy6+/yVhZKT3B/QpP1BZaesGqasf09HPOBzRarcDIFH0WwXlXQfE7q7ipxK4MSiC5DIBWURCny6fw==", + "license": "MIT", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-remove-scroll": { "version": "2.7.2", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", diff --git a/package.json b/package.json index 8cfef2cd..7d3ac823 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "mermaid": "^11.16.0", "react": "^19.2.7", "react-dom": "^19.2.7", + "react-icons": "^5.7.0", "react-server-dom-webpack": "^19.2.7", "rehype-katex": "^7.0.1", "remark-math": "^6.0.0", diff --git a/press.config.tsx b/press.config.tsx index aa93b381..cab5283e 100644 --- a/press.config.tsx +++ b/press.config.tsx @@ -7,22 +7,10 @@ import { takumiPlugin } from "fumapress/plugins/takumi"; import { createNotebookLayoutPage } from "fumapress/layouts/notebook"; import defaultMdxComponents, { createRelativeLink } from "fumadocs-ui/mdx"; import { docs } from "./.source/server"; +import { BrandLogo } from "./src/components/brand-logo"; import { Mermaid } from "./src/components/mermaid"; import { Tab, Tabs } from "./src/components/tabs"; -// Lucide icons registered for use in MDX (e.g. } />). -import { - Rocket, - Container, - BookOpen, - Network, - Gauge, - Server, - ClipboardCheck, - Activity, - Code, - Terminal, - Library, -} from "lucide-react"; +import { ArrowRight } from "lucide-react"; const isDev = import.meta.env.DEV; @@ -225,18 +213,8 @@ gtag('config', 'G-TWW7WMTWL9');`, Mermaid, Tab, Tabs, - // Lucide icons for } /> on landing pages. - Rocket, - Container, - BookOpen, - Network, - Gauge, - Server, - ClipboardCheck, - Activity, - Code, - Terminal, - Library, + BrandLogo, + ArrowRight, }; }, }), diff --git a/public/_redirects b/public/_redirects new file mode 100644 index 00000000..3abeb90b --- /dev/null +++ b/public/_redirects @@ -0,0 +1,40 @@ +/installation/console-first-steps /administration/console 301 +/administration/data/data-tiering /administration/data/tiered-storage 301 +/management /administration 301 +/management/* /administration/:splat 301 +/administration/iam /security-compliance/iam 301 +/administration/iam/oidc /security-compliance/oidc 301 +/administration/iam/* /security-compliance/iam/:splat 301 +/administration/security/encryption /security-compliance/encryption 301 +/administration/security/encryption/* /security-compliance/encryption/:splat 301 +/administration/security/sts-token-service /security-compliance/iam/sts 301 +/administration/security/oidc /security-compliance/oidc 301 +/administration/security/audit-logs /security-compliance/audit-logs 301 +/security-compliance/iam/oidc /security-compliance/oidc 301 +/security-compliance/sts-token-service /security-compliance/iam/sts 301 +/operations/cluster-health-check /operations/status-check 301 +/reference/ports /operations/status-check 301 +/reference/metrics /operations/observability 301 +/concepts/principle/erasure-coding /installation/requirement/ec-configuration 301 +/integration/nginx /developer/integration/reverse-proxy/nginx 301 +/concepts/architecture / 301 +/concepts/comparison / 301 +/concepts/glossary /reference 301 +/concepts/introduction / 301 +/concepts/limit /reference 301 +/developer/examples/mc /operations/rc 301 +/developer/mc /operations/rc 301 +/installation/checklists /installation/requirement/checklists 301 +/installation/checklists/hardware-checklists /installation/requirement/checklists/hardware-checklists 301 +/installation/checklists/hardware-selection /installation/requirement/checklists/hardware-selection 301 +/installation/checklists/network-checklists /installation/requirement/checklists/network-checklists 301 +/installation/checklists/security-checklists /installation/requirement/checklists/security-checklists 301 +/installation/checklists/software-checklists /installation/requirement/checklists/software-checklists 301 +/installation/docker /installation/container/docker 301 +/operations/cold-start /operations/status-check 301 +/operations/decommission /operations/scaling/storage-pool-decommission 301 +/operations/monitoring /operations/observability 301 +/troubleshooting/healing /operations/high-availability/node-healing 301 +/upgrade-scale /operations/scaling 301 +/upgrade-scale/availability-and-resiliency /operations/scaling 301 +/upgrade-scale/upgrade /operations/upgrade 301 diff --git a/src/app.css b/src/app.css index ef7b5f4b..f647fb65 100644 --- a/src/app.css +++ b/src/app.css @@ -49,3 +49,242 @@ border-radius: 0; background: var(--color-fd-background); } + +.rustfs-install-home { + --rustfs-install-accent: #d61f45; + margin-top: 1.5rem; +} + +article:has(.rustfs-install-home) > h1 { + max-width: 48rem; + font-size: 3.75rem; + line-height: 1.08; +} + +article:has(.rustfs-install-home) > h1 + p { + max-width: 48rem; + font-size: 1.25rem; + line-height: 1.65; +} + +article:has(.rustfs-install-home) > h1 + p + div { + display: none; +} + +.rustfs-install-intro { + max-width: 52rem; + margin-bottom: 2.75rem; +} + +.rustfs-install-lede { + margin: 0; + color: var(--color-fd-muted-foreground); + font-size: 1.125rem; + line-height: 1.75; +} + +.rustfs-install-overview { + display: inline-flex; + align-items: center; + gap: 0.5rem; + margin-top: 1.25rem; + color: var(--rustfs-install-accent); + font-weight: 650; + text-decoration: none; +} + +.rustfs-install-overview > svg { + width: 1rem; + height: 1rem; + transition: transform 160ms ease; +} + +.rustfs-install-overview:hover > svg { + transform: translateX(0.2rem); +} + +.rustfs-install-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 1rem; +} + +.rustfs-install-card { + position: relative; + display: grid; + grid-template-columns: 3rem minmax(0, 1fr) 1.25rem; + gap: 1rem; + align-items: start; + min-height: 10.5rem; + padding: 1.5rem; + overflow: hidden; + border: 1px solid var(--color-fd-border); + border-radius: 0.5rem; + background: + linear-gradient(135deg, color-mix(in srgb, var(--rustfs-install-accent) 4%, transparent), transparent 42%), + var(--color-fd-card); + color: var(--color-fd-foreground); + text-decoration: none; + box-shadow: 0 1px 2px color-mix(in srgb, var(--color-fd-foreground) 5%, transparent); + transition: border-color 160ms ease, box-shadow 160ms ease, transform 160ms ease; +} + +.rustfs-install-card:hover { + border-color: color-mix(in srgb, var(--rustfs-install-accent) 58%, var(--color-fd-border)); + box-shadow: 0 10px 24px color-mix(in srgb, var(--color-fd-foreground) 9%, transparent); + transform: translateY(-2px); +} + +.rustfs-install-card:focus-visible, +.rustfs-install-overview:focus-visible { + outline: 2px solid var(--color-fd-ring); + outline-offset: 3px; +} + +.rustfs-install-icon { + --rustfs-brand-color: var(--rustfs-install-accent); + display: inline-flex; + align-items: center; + justify-content: center; + width: 3rem; + height: 3rem; + border: 1px solid color-mix(in srgb, var(--rustfs-brand-color) 28%, var(--color-fd-border)); + border-radius: 0.5rem; + background: color-mix(in srgb, var(--rustfs-brand-color) 9%, var(--color-fd-background)); + color: var(--rustfs-brand-color); +} + +.rustfs-install-icon > svg { + width: 1.5rem; + height: 1.5rem; +} + +.rustfs-install-icon--linux { + --rustfs-brand-color: #171717; +} + +.rustfs-install-icon--docker { + --rustfs-brand-color: #2496ed; +} + +.rustfs-install-icon--podman { + --rustfs-brand-color: #892ca0; +} + +.rustfs-install-icon--helm { + --rustfs-brand-color: #0f1689; +} + +.rustfs-install-icon--kubernetes { + --rustfs-brand-color: #326ce5; +} + +.rustfs-install-icon--apple { + --rustfs-brand-color: #171717; +} + +.rustfs-install-icon--windows { + --rustfs-brand-color: #0078d4; +} + +.rustfs-install-copy { + display: flex; + min-width: 0; + flex-direction: column; + gap: 0.625rem; +} + +.rustfs-install-copy > strong { + font-size: 1.0625rem; + line-height: 1.35; +} + +.rustfs-install-copy > span { + color: var(--color-fd-muted-foreground); + font-size: 0.9375rem; + line-height: 1.6; +} + +.rustfs-install-arrow { + width: 1.125rem; + height: 1.125rem; + margin-top: 0.1875rem; + color: var(--color-fd-muted-foreground); + transition: color 160ms ease, transform 160ms ease; +} + +.rustfs-install-card:hover .rustfs-install-arrow { + color: var(--rustfs-install-accent); + transform: translateX(0.2rem); +} + +@media (max-width: 48rem) { + article:has(.rustfs-install-home) > h1 { + font-size: 2.5rem; + } + + article:has(.rustfs-install-home) > h1 + p { + font-size: 1.0625rem; + } + + .rustfs-install-intro { + margin-bottom: 2rem; + } + + .rustfs-install-grid { + grid-template-columns: minmax(0, 1fr); + } + + .rustfs-install-card { + min-height: 9.5rem; + } +} + +@media (max-width: 30rem) { + .rustfs-install-lede { + font-size: 1rem; + } + + .rustfs-install-card { + grid-template-columns: 2.75rem minmax(0, 1fr); + padding: 1.25rem; + } + + .rustfs-install-icon { + width: 2.75rem; + height: 2.75rem; + } + + .rustfs-install-arrow { + display: none; + } +} + +.dark .rustfs-install-home { + --rustfs-install-accent: #ff5574; +} + +.dark .rustfs-install-icon--linux, +.dark .rustfs-install-icon--apple { + --rustfs-brand-color: #f5f5f5; +} + +.dark .rustfs-install-icon--docker { + --rustfs-brand-color: #59b9ff; +} + +.dark .rustfs-install-icon--podman { + --rustfs-brand-color: #c77dda; +} + +.dark .rustfs-install-icon--helm { + --rustfs-brand-color: #929bff; +} + +.dark .rustfs-install-icon--kubernetes { + --rustfs-brand-color: #78a4ff; +} + +.dark .rustfs-install-icon--windows { + --rustfs-brand-color: #4bb4ff; +} diff --git a/src/components/brand-logo.tsx b/src/components/brand-logo.tsx new file mode 100644 index 00000000..6b9a6d84 --- /dev/null +++ b/src/components/brand-logo.tsx @@ -0,0 +1,20 @@ +"use client"; + +import { FaApple, FaLinux, FaWindows } from "react-icons/fa6"; +import { SiDocker, SiHelm, SiKubernetes, SiPodman } from "react-icons/si"; + +const logos = { + apple: FaApple, + docker: SiDocker, + helm: SiHelm, + kubernetes: SiKubernetes, + linux: FaLinux, + podman: SiPodman, + windows: FaWindows, +} as const; + +export function BrandLogo({ name }: { name: keyof typeof logos }) { + const Logo = logos[name]; + + return