> ## Documentation Index
> Fetch the complete documentation index at: https://docs.controlplane.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SeaweedFS

> Deploy SeaweedFS on Control Plane as a single all-in-one node serving S3-compatible object storage with signed-request authentication, persistent storage, startup buckets and an admin UI, into an existing GVC. Two dictionary secrets (S3 keys and admin login) must exist before install.

## Overview

SeaweedFS is a distributed object store with an S3-compatible API. This template deploys a single all-in-one node — master, volume server, filer, S3 gateway and admin UI in one `weed mini` process — with persistent storage, startup bucket creation and signed-request (SigV4) authentication on port `8333`.

Its main use is as an in-org storage target: any workload or template that accepts an S3-compatible endpoint and a static access key pair can point at it without data leaving your organization. Both sets of credentials — the S3 keys and the admin UI login — come from [dictionary secrets](/guides/create-secret/dictionary) you create before installing; the template creates no secrets of its own.

<Note>
  This template deploys into an **existing GVC** that you already have. It does not create or manage a GVC. Pass the GVC with `--gvc GVC_NAME` at install time.
</Note>

### What Gets Created

| Resource | Name | Purpose | Optional |
| - | - | - | - |
| Workload | `RELEASE_NAME-seaweedfs` | All-in-one SeaweedFS node (stateful, 1 replica); S3 API on port `8333`, admin UI on port `23646` | — |
| Volume set | `RELEASE_NAME-seaweedfs-data` | Persistent disk at `/data` for objects, filer metadata and master metadata (20 GiB by default); a final snapshot is kept for 7 days on deletion | — |
| Identity | `RELEASE_NAME-seaweedfs-identity` | Workload identity used to read the prerequisite secrets | — |
| Policy | `RELEASE_NAME-seaweedfs-policy` | Grants the identity `reveal` on your S3 credentials secret, plus your admin credentials secret while `adminUI.enabled` is `true` | — |

The admin UI port `23646` is only declared on the workload when `adminUI.enabled` is `true` (the default).

## Prerequisites

**Two secrets must exist before you install.** They are deliberately separate: the S3 keys are handed to every client application, while the admin login guards the bucket browser and maintenance tools. Secrets are org-level, so no `--gvc` flag is involved.

<Steps>
  <Step title="Create the S3 credentials secret">
    A [dictionary secret](/guides/create-secret/dictionary) holding exactly the keys `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. Every S3 client uses these, and SeaweedFS serves S3 with *no authentication at all* when they are absent:

    ```bash theme={null}
    cpln secret create-dictionary --name S3_CREDENTIALS_SECRET_NAME \
      --entry AWS_ACCESS_KEY_ID="$(openssl rand -hex 10)" \
      --entry AWS_SECRET_ACCESS_KEY="$(openssl rand -hex 24)"
    ```

    Set `s3.credentialsSecretName` to the name you used.
  </Step>

  <Step title="Create the admin UI credentials secret">
    A [dictionary secret](/guides/create-secret/dictionary) holding exactly the keys `username` and `password`, guarding the admin login form. Required whenever `adminUI.enabled` is `true`, which is the default:

    ```bash theme={null}
    cpln secret create-dictionary --name ADMIN_CREDENTIALS_SECRET_NAME \
      --entry username=admin \
      --entry password="$(openssl rand -hex 24)"
    ```

    Set `adminUI.credentialsSecretName` to the name you used.
  </Step>

  <Step title="Read either secret back later">
    Pass `-o yaml`; a bare `cpln secret reveal` prints only a summary table, not the values:

    ```bash theme={null}
    cpln secret reveal ADMIN_CREDENTIALS_SECRET_NAME -o yaml
    ```
  </Step>
</Steps>

<Warning>
  **A missing prerequisite secret wedges the install rather than failing it.** `cpln helm install` reports success and all four resources are created, but the workload never starts. Because the container never ran, `cpln logs` returns **zero lines**, which looks like a platform fault rather than a missing step.

  The only place the missing secret is named is `status.versions[].message`:

  ```bash theme={null}
  cpln workload get-deployments RELEASE_NAME-seaweedfs --gvc GVC_NAME -o yaml
  ```

  ```text theme={null}
  The secret ADMIN_CREDENTIALS_SECRET_NAME no longer exists. Workload updates are
  paused until the secret is added or the reference to the secret removed.
  ```

  Use **`get-deployments`** — plain `cpln workload get` has no `versions` key. Once the secret exists the deployment recovers on its own after several minutes, or immediately with a forced redeployment:

  ```bash theme={null}
  cpln workload force-redeployment RELEASE_NAME-seaweedfs --gvc GVC_NAME
  ```
</Warning>

## Installation

With both secrets in place, install into your existing GVC, passing the names of the two secrets you created above:

```bash theme={null}
cpln helm install RELEASE_NAME oci://ghcr.io/controlplane-com/templates/seaweedfs \
  --version 1.1.0 --gvc GVC_NAME \
  --set s3.credentialsSecretName=S3_CREDENTIALS_SECRET_NAME \
  --set adminUI.credentialsSecretName=ADMIN_CREDENTIALS_SECRET_NAME
```

<CardGroup cols={2}>
  <Card title="UI" href="/template-catalog/install-manage/ui" icon="laptop">
    Browse, install, and manage templates visually
  </Card>

  <Card title="CLI" href="/template-catalog/install-manage/cli" icon="terminal">
    Manage templates from your terminal
  </Card>

  <Card title="Terraform" href="/template-catalog/install-manage/terraform" icon={<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"><g fill-rule="evenodd"><path d="M77.941 44.5v36.836L46.324 62.918V26.082zm0 0" fill="#5c4ee5"/><path d="M81.41 81.336l31.633-18.418V26.082L81.41 44.5zm0 0" fill="#4040b2"/><path d="M11.242 42.36L42.86 60.776V23.941L11.242 5.523zm0 0M77.941 85.375L46.324 66.957v36.82l31.617 18.418zm0 0" fill="#5c4ee5"/></g></svg>}>
    Declare templates in your Terraform configurations
  </Card>

  <Card
    title="Pulumi"
    href="/template-catalog/install-manage/pulumi"
    icon={<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" id="Pulumi-Icon--Streamline-Svg-Logos" height="24" width="24">
    <desc>
        Pulumi Icon Streamline Icon: https://streamlinehq.com
    </desc>
    <path fill="#f26e7e" d="M4.683025 13.3318c0.869125 -0.5018 0.870575 -2.1264 0.003225 -3.62865s-2.27504 -2.313275 -3.1441725 -1.811475C0.672945 8.3935 0.6715 10.0181 1.53885 11.52035c0.86735 1.502275 2.27505 2.313275 3.144175 1.81145Zm0.0052 3.2167c0.86735 1.502275 0.865925 3.126875 -0.003225 3.628675 -0.86915 0.5018 -2.2768275 -0.309225 -3.144175 -1.81145 -0.8673525 -1.50225 -0.8659075 -3.126875 0.003225 -3.628675 0.8691325 -0.5018 2.276825 0.309225 3.144175 1.81145Zm5.922875 3.4243c0.86735 1.50225 0.8659 3.126775 -0.003225 3.62875 -0.869125 0.501775 -2.27685 -0.309325 -3.1442 -1.81155 -0.867325 -1.50225 -0.865875 -3.12685 0.00325 -3.628675 0.869125 -0.5018 2.276825 0.309225 3.144175 1.811475Zm-0.001925 -6.845275c0.86735 1.50225 0.8659 3.12685 -0.003225 3.628675 -0.869125 0.5018 -2.276825 -0.309225 -3.144175 -1.811475 -0.86735 -1.50225 -0.8659 -3.12685 0.003225 -3.62865 0.869125 -0.501825 2.276825 0.3092 3.144175 1.81145Z" stroke-width="0.25"></path>
    <path fill="#8a3391" d="M22.45775 11.524125c0.86725 -1.502225 0.865925 -3.12685 -0.003225 -3.62865 -0.869125 -0.501825 -2.276825 0.3092 -3.144175 1.811475 -0.86735 1.50225 -0.8659 3.126825 0.003225 3.62865 0.869125 0.501825 2.276825 -0.3092 3.144175 -1.811475Zm0.000175 3.2151c0.869075 0.5018 0.870625 2.1264 0.003225 3.62865 -0.86735 1.50225 -2.27505 2.313275 -3.144175 1.81145 -0.869125 -0.5018 -0.870575 -2.126425 -0.003225 -3.62865 0.86735 -1.50225 2.27505 -2.313275 3.144175 -1.81145ZM16.536225 18.157875c0.86915 0.501825 0.8706 2.126425 0.00325 3.628675 -0.86735 1.502125 -2.275075 2.313225 -3.1442 1.81145 -0.869125 -0.50175 -0.870575 -2.126425 -0.003225 -3.62865 0.867375 -1.502275 2.27505 -2.3133 3.144175 -1.811475Zm-0.003325 -6.843775c0.869125 0.5018 0.870575 2.126425 0.003225 3.628675s-2.27505 2.313275 -3.1442 1.811475c-0.869125 -0.501825 -0.870575 -2.126425 -0.003225 -3.628675 0.86735 -1.502275 2.27505 -2.313275 3.1442 -1.811475Z" stroke-width="0.25"></path>
    <path fill="#f7bf2a" d="M15.138225 2.06721c0 1.003615 -1.40625 1.817215 -3.14095 1.817215 -1.7347 0 -3.14095 -0.8136 -3.14095 -1.817215C8.856325 1.06359 10.262575 0.25 11.997275 0.25c1.7347 0 3.14095 0.81359 3.14095 1.81721ZM9.2166 5.482375c0 1.003625 -1.40625 1.8172 -3.14095 1.8172 -1.7347 0 -3.14095 -0.813575 -3.14095 -1.8172s1.40625 -1.817225 3.14095 -1.817225c1.7347 0 3.14095 0.8136 3.14095 1.817225Zm8.71005 1.8172c1.7347 0 3.14095 -0.813575 3.14095 -1.8172s-1.40625 -1.817225 -3.14095 -1.817225c-1.7347 0 -3.14095 0.8136 -3.14095 1.817225s1.40625 1.8172 3.14095 1.8172Zm-2.788425 1.605625c0 1.003625 -1.40625 1.8172 -3.14095 1.8172 -1.7347 0 -3.14095 -0.813575 -3.14095 -1.8172 0 -1.0036 1.40625 -1.8172 3.14095 -1.8172 1.7347 0 3.14095 0.8136 3.14095 1.8172Z" stroke-width="0.25"></path>
    </svg>}
  >
    Declare templates in your Pulumi programs
  </Card>
</CardGroup>

<Warning>
  **Upgrading a release installed with `1.0.0` is a breaking change.** `adminUI.username` and `adminUI.password` no longer exist, and an upgrade that still sets either one is refused before anything is applied. Follow the upgrade notes under [Operations](#operations) first.
</Warning>

## Configuration

### Image and Resources

```yaml theme={null}
image: chrislusf/seaweedfs:4.40

resources:
  minCpu: 250m
  maxCpu: 1000m
  minMemory: 512Mi
  maxMemory: 2Gi # raise for stores holding tens of millions of objects (in-memory volume index)
```

`weed mini` is the image's own default command; the template pins every port explicitly because the port layout can change between upstream releases. Pin a concrete released tag, never `latest`.

### Storage

```yaml theme={null}
volumeset:
  capacity: 20 # initial capacity in GiB (minimum is 10)
  autoscaling:
    enabled: false # set to true to grow the volume automatically as it fills
    maxCapacity: 200 # maximum capacity in GiB when autoscaling is enabled
    minFreePercentage: 10 # minimum free percentage that triggers scaling
    scalingFactor: 1.2 # how much to grow the volume when scaling is triggered
```

One volume holds object data, filer metadata and master metadata. The chart refuses to render when `capacity` is below 10 or when autoscaling is enabled with `maxCapacity` smaller than `capacity`. Data survives restarts, redeployments and `helm upgrade` under the same release name; uninstalling deletes the volume set and every stored object, keeping a final snapshot for 7 days.

<Note>
  SeaweedFS derives its volume file size from the disk capacity at startup, so growing the volume set takes effect on the next restart. This is harmless: SeaweedFS simply creates more volume files.
</Note>

### S3 API

```yaml theme={null}
s3:
  # PREREQUISITE dictionary secret — MUST EXIST BEFORE INSTALL, or the deployment
  # waits on it forever and looks broken. These are the S3 credentials every client
  # uses; SeaweedFS serves S3 with NO authentication if they are absent.
  credentialsSecretName: my-seaweedfs-s3-credentials
  # Buckets created at startup if they do not already exist, e.g. [backups, uploads].
  # Most backup tools require the bucket to exist before they run.
  buckets: []
```

`s3.buckets` entries must be lowercase letters, digits, dots and hyphens, 3–63 characters, starting with a letter or digit; the chart refuses to render otherwise. Buckets are only ever created, never deleted — removing a name from the list leaves the bucket in place, and adding one takes effect on the next restart. You can also create buckets at any time through the admin UI or with `aws s3 mb`.

### Admin UI

```yaml theme={null}
adminUI:
  enabled: true # cluster status, bucket browser, user and maintenance management
  # REQUIRED PREREQUISITE SECRET when adminUI.enabled is true — CREATE IT BEFORE
  # YOU INSTALL. A `dictionary` secret holding exactly two keys, `username` and
  # `password`, guarding the admin login form. If it does not exist at install
  # time the deployment WEDGES silently — `cpln logs` returns nothing at all.
  credentialsSecretName: my-seaweedfs-admin-credentials
```

The admin UI listens on port `23646` and is reachable from inside the GVC only; it is never publicly routed. Setting `adminUI.enabled: false` removes the port and the credential references from the workload and narrows the policy to the S3 credentials secret alone — the admin routes then return `404`. With the UI disabled, `credentialsSecretName` may be left empty.

### Access

```yaml theme={null}
publicAccess:
  enabled: false # true = S3 API on the auto *.cpln.app HTTPS endpoint (path-style addressing)
internalAccess: # internal firewall scope
  type: same-gvc # options: none, same-gvc, same-org, workload-list
  workloads: [] # only used when type is workload-list
  # workloads:
  #   - //gvc/GVC_NAME/workload/WORKLOAD_NAME
```

| `internalAccess.type` | Description |
| - | - |
| `none` | No internal access. |
| `same-gvc` | Allow access from all workloads in the same GVC (default). |
| `same-org` | Allow access from all workloads in the same organization, including other GVCs. |
| `workload-list` | Allow access only from the workloads listed in `internalAccess.workloads`. |

`publicAccess.enabled: true` serves the S3 API over HTTPS on the automatically assigned `*.cpln.app` canonical endpoint, using path-style addressing. Only port `8333` is exposed this way — the admin UI is never publicly routed. Access changes can take a few minutes to take effect.

## Connecting

| Target | Address | Notes |
| - | - | - |
| S3 API, inside the GVC | `http://RELEASE_NAME-seaweedfs.GVC_NAME.cpln.local:8333` | Plain `http://`; path-style addressing. |
| S3 API, host:port form | `RELEASE_NAME-seaweedfs.GVC_NAME.cpln.local:8333` | For clients that take a bare host:port (Thanos, Mimir, Prometheus, DuckDB). Pair with their `insecure: true` or equivalent. |
| S3 API, public | `https://<canonical>.cpln.app` | Only with `publicAccess.enabled: true`; port 443, no port suffix. Read it from `status.canonicalEndpoint`. |
| Admin UI | `http://RELEASE_NAME-seaweedfs.GVC_NAME.cpln.local:23646` | Inside the GVC only. From your machine, use the port-forward below. |
| Health check | `GET /healthz` on port `8333` | Unauthenticated by design. |
| S3 credentials | `cpln secret reveal S3_CREDENTIALS_SECRET_NAME -o yaml` | `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`. |
| Admin UI credentials | `cpln secret reveal ADMIN_CREDENTIALS_SECRET_NAME -o yaml` | `username` / `password`. |

Find the canonical hostname with:

```bash theme={null}
cpln workload get RELEASE_NAME-seaweedfs --gvc GVC_NAME -o yaml
```

Verify the S3 API from your machine through a tunnel — it works even when the workload is closed to the internet. Export the two keys from your S3 credentials secret first, then:

```bash theme={null}
cpln port-forward RELEASE_NAME-seaweedfs 8333:8333 --gvc GVC_NAME
```

```bash theme={null}
aws configure set default.s3.addressing_style path
aws --endpoint-url http://localhost:8333 s3 ls
aws --endpoint-url http://localhost:8333 s3 cp ./file s3://BUCKET/file
```

Requests without a valid signature are rejected: unsigned requests get `403`, and a wrong secret key gets `SignatureDoesNotMatch`.

To reach the admin UI from your machine:

```bash theme={null}
cpln port-forward RELEASE_NAME-seaweedfs 23646:23646 --gvc GVC_NAME
```

Then open `http://localhost:23646` and log in with the `username` and `password` from your admin credentials secret.

### Using SeaweedFS as an S3 Backend

Any client that speaks S3 works, subject to three rules:

* **Path-style addressing is required.** Objects are addressed as `http://RELEASE_NAME-seaweedfs.GVC_NAME.cpln.local:8333/BUCKET/KEY`; virtual-host style (`BUCKET.host`) is not served. Set `forcePathStyle: true` / `addressing_style = path` on the client.
* **Any region value works.** The region is read from the client's signature scope and never compared against a server-side value, so a consumer hardcoded to `us-east-1` is fine as-is.
* **The bucket must already exist** for most backup tools. Create it with `s3.buckets`, through the admin UI, or with `aws s3 mb`.

Endpoint form and credential shape for the common catalog consumers. In every case the two credential values are the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` from your S3 credentials secret — most consumers want them in a second dictionary secret under their own key names:

| Consumer template | Endpoint value | Credentials |
| - | - | - |
| [postgres-highly-available](/template-catalog/templates/postgres-highly-available), [timescaledb-highly-available](/template-catalog/templates/timescaledb-highly-available) | `backup.provider: minio`, `backup.minio.endpoint: http://RELEASE_NAME-seaweedfs.GVC_NAME.cpln.local:8333` | `backup.minio.credentialsSecretName` → a dictionary secret with keys `accessKey` and `secretKey` |
| [thanos](/template-catalog/templates/thanos), [mimir](/template-catalog/templates/mimir) | `storage.type: minio`, `storage.minio.endpoint: RELEASE_NAME-seaweedfs.GVC_NAME.cpln.local:8333` (no scheme), `storage.minio.insecure: true` | `storage.minio.credentialsSecretName` → a dictionary secret with keys `accessKey` and `secretKey` |
| [prometheus](/template-catalog/templates/prometheus) | Same fields nested under the Thanos sidecar: `thanos.objectStorage.type: minio`, `thanos.objectStorage.minio.endpoint`, `thanos.objectStorage.minio.insecure: true` | `thanos.objectStorage.minio.credentialsSecretName` → a dictionary secret with keys `accessKey` and `secretKey` |
| [sftpgo](/template-catalog/templates/sftpgo) | `storage.type: minio`, `storage.minio.endpoint: http://RELEASE_NAME-seaweedfs.GVC_NAME.cpln.local:8333` | `storage.minio.accessKey` and `storage.minio.accessSecret` as values |
| [docmost](/template-catalog/templates/docmost) | `storage.type: s3`, `storage.s3.endpoint: http://RELEASE_NAME-seaweedfs.GVC_NAME.cpln.local:8333`, `storage.s3.forcePathStyle: true` | `storage.s3.auth.secretName` → a dictionary secret with keys `AWS_S3_ACCESS_KEY_ID` and `AWS_S3_SECRET_ACCESS_KEY` |
| [n8n](/template-catalog/templates/n8n), [metabase](/template-catalog/templates/metabase), [keycloak](/template-catalog/templates/keycloak), [unleash](/template-catalog/templates/unleash) | Backups of their bundled database: `postgresHA.backup.provider: minio` (or `postgres.backup.provider: minio` in single-instance mode) with the matching `…backup.minio.endpoint` | `…backup.minio.credentialsSecretName` → a dictionary secret with keys `accessKey` and `secretKey` |
| [polaris](/template-catalog/templates/polaris), [duckdb](/template-catalog/templates/duckdb) | See each page — both document SeaweedFS as a tested target. | — |

Each consumer's own page carries the exact create-secret command for its credentials secret. Templates whose object-storage support is limited to named cloud providers — [ghost](/template-catalog/templates/ghost) and [clickhouse](/template-catalog/templates/clickhouse) among them — have no arbitrary-endpoint option and cannot use this template as their storage target.

## Operations

### Backing Up

Everything SeaweedFS stores — objects and all metadata — lives on the single `RELEASE_NAME-seaweedfs-data` volume set, so a volume set snapshot captures the whole store consistently. Take one on demand:

```bash theme={null}
cpln volumeset snapshot create RELEASE_NAME-seaweedfs-data --gvc GVC_NAME --snapshot-name NAME
```

```bash theme={null}
cpln volumeset snapshot get RELEASE_NAME-seaweedfs-data --gvc GVC_NAME
```

For a scheduled snapshot policy, see the [volume set reference](/reference/volumeset#snapshots). A final snapshot is also taken automatically when the volume set is deleted and kept for 7 days.

For an object-level copy that any S3 tool can read, mirror a bucket with a standard S3 client over the tunnel from [Connecting](#connecting):

```bash theme={null}
aws --endpoint-url http://localhost:8333 s3 sync s3://BUCKET ./BUCKET-copy
```

### Restoring a Backup

<Warning>
  **Restoring a volume set snapshot onto this template has not been verified.** The snapshot captures the disk, but restoring it into a running release — including how the single replica is re-attached to the restored volume — has not been exercised on this chart. Treat volume set snapshots as a safety net rather than a tested recovery path until you have rehearsed a restore yourself. See the [volume set reference](/reference/volumeset) for the platform's snapshot commands.
</Warning>

Restoring an object-level copy is an ordinary upload: create the bucket if it does not exist (through `s3.buckets`, the admin UI or `aws s3 mb`), then sync the copy back:

```bash theme={null}
aws --endpoint-url http://localhost:8333 s3 sync ./BUCKET-copy s3://BUCKET
```

### Upgrading from 1.0.0

Version `1.1.0` moved the admin UI login out of Helm values. `1.0.0` shipped a working username and password as values, used exactly as written — a published default guarding the bucket browser and maintenance tools for the life of the install.

| | `1.0.0` | `1.1.0` |
| - | - | - |
| Admin UI login | `adminUI.username` and `adminUI.password` values | `adminUI.credentialsSecretName` — a dictionary secret you create with `username` and `password` |
| Chart-created secret | Held the admin credentials | None; the template creates no secrets |
| S3 credentials | `s3.credentialsSecretName` prerequisite secret | Unchanged |

<Warning>
  **An upgrade that still carries either removed key is rejected at render time, before anything is applied** — and the guard fires even when `adminUI.enabled` is `false`, so a stale `password:` in your values cannot be silently accepted. The running release is left untouched:

  ```text theme={null}
  adminUI.username and adminUI.password were removed in seaweedfs 1.1.0 — the admin UI login form is
  now guarded by a REQUIRED prerequisite dictionary secret. Create one holding the keys `username` and
  `password`, then set adminUI.credentialsSecretName to its name.
  ```

  Leaving `adminUI.credentialsSecretName` empty while the UI is enabled is refused the same way.
</Warning>

<Steps>
  <Step title="Create the admin credentials secret">
    Follow [Prerequisites](#prerequisites). Put your existing username and password into it if current logins should keep working; otherwise choose new ones — the shipped `1.0.0` default was public.
  </Step>

  <Step title="Drop the removed keys from your values">
    Remove `adminUI.username` and `adminUI.password`, and set `adminUI.credentialsSecretName` instead. Leave `s3.credentialsSecretName` exactly as it is — the S3 half is unchanged, and anything already backing up into SeaweedFS needs no edit.
  </Step>

  <Step title="Upgrade the release">
    Run `cpln helm upgrade` with `--version 1.1.0` and your updated values, as described in the [CLI guide](/template-catalog/install-manage/cli#manage-a-template). The single replica restarts and the S3 API is unavailable until it is back; stored objects on the volume set are untouched.
  </Step>
</Steps>

### Rotating Credentials

Both secrets are read when a replica starts, so rotating one takes effect only after a redeployment. Update the secret's values, then force a redeployment:

```bash theme={null}
cpln workload force-redeployment RELEASE_NAME-seaweedfs --gvc GVC_NAME
```

Rotating the S3 credentials secret genuinely rotates the keys — the S3 identity is rebuilt from the environment on every boot rather than stored on disk. Once the new replica is serving, the old access key is rejected with `InvalidAccessKeyId` and stored objects are untouched. Update every consumer at the same time.

### Scaling and Availability

The template deploys a **single replica by design** and exposes no replica count. `weed mini` runs one master, one filer and one volume server in a single process, so additional replicas would each hold a separate, divergent object store behind one name. Consequences:

* A redeploy, `helm upgrade` or platform reschedule is a full S3 outage until the replica is back. Schedule upgrades accordingly.
* Durability comes from the persistent volume set, not from replication: data survives restarts and reschedules, and a node failure re-attaches the same volume.
* Grow capacity vertically — raise `resources.maxMemory` for very large object counts and `volumeset.capacity` (or enable `volumeset.autoscaling`) for more data.

### Troubleshooting

<AccordionGroup>
  <Accordion title="Deployment never starts and cpln logs returns nothing">
    **Symptom:** `cpln helm install` succeeded, but the workload sits at zero replicas and `cpln logs '{gvc="GVC_NAME", workload="RELEASE_NAME-seaweedfs"}' --limit 50 --since 10m` returns no lines at all.

    **Cause:** One of the two prerequisite secrets does not exist, so the container is never started. The deployment message names it:

    ```bash theme={null}
    cpln workload get-deployments RELEASE_NAME-seaweedfs --gvc GVC_NAME -o yaml
    ```

    ```text theme={null}
    The secret S3_CREDENTIALS_SECRET_NAME no longer exists. Workload updates are
    paused until the secret is added or the reference to the secret removed.
    ```

    **Fix:** Create the missing secret as shown in [Prerequisites](#prerequisites). The deployment recovers on its own after several minutes, or immediately with `cpln workload force-redeployment RELEASE_NAME-seaweedfs --gvc GVC_NAME`.
  </Accordion>

  <Accordion title="helm upgrade fails with 'adminUI.username and adminUI.password were removed in seaweedfs 1.1.0'">
    **Symptom:** The upgrade stops at render time with that message and no revision is created.

    **Cause:** Your values still carry `adminUI.username` or `adminUI.password`, which `1.1.0` removed. The guard fires even with `adminUI.enabled: false`.

    **Fix:** Follow the steps under Upgrading from 1.0.0 above: create the admin credentials secret, remove both keys from your values, set `adminUI.credentialsSecretName`, then upgrade again.
  </Accordion>

  <Accordion title="S3 client gets 403 or SignatureDoesNotMatch">
    **Symptom:** `aws s3 ls` or a consumer template's backup job is refused with `403` or an error body containing `SignatureDoesNotMatch`.

    **Cause:** The request is unsigned, or the client's secret key does not match the value in your S3 credentials secret. A client using virtual-host-style addressing (`bucket.host`) also fails, because only path-style is served.

    **Fix:** Compare the client's keys against `cpln secret reveal S3_CREDENTIALS_SECRET_NAME -o yaml`, and set path-style addressing on the client (`aws configure set default.s3.addressing_style path`, `forcePathStyle: true`, or the consumer's equivalent).
  </Accordion>

  <Accordion title="S3 client gets InvalidAccessKeyId after rotating the secret">
    **Symptom:** Clients that worked before now fail with `InvalidAccessKeyId`.

    **Cause:** The S3 credentials secret was rotated and the node redeployed, so the old access key is no longer valid; the client still holds the old key.

    **Fix:** Update every consumer with the new `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`. Stored objects are unaffected.
  </Accordion>

  <Accordion title="Admin UI returns 404 or is unreachable">
    **Symptom:** Requests to port `23646` return `404`, or the port cannot be reached at all.

    **Cause:** With `adminUI.enabled: false` the admin routes are unregistered and the port is not declared on the workload. With it enabled, the UI is still never publicly routed — `publicAccess.enabled: true` exposes port `8333` only, and requesting `/login` on the public endpoint reaches the S3 gateway instead.

    **Fix:** Set `adminUI.enabled: true` (with `adminUI.credentialsSecretName` pointing at an existing secret), and reach the UI with `cpln port-forward RELEASE_NAME-seaweedfs 23646:23646 --gvc GVC_NAME` or from a workload inside the GVC.
  </Accordion>

  <Accordion title="Changing publicAccess or internalAccess appears to have no effect">
    **Symptom:** Right after a `helm upgrade` that flipped an access setting, requests still get the old answer.

    **Cause:** Firewall changes take a few minutes to propagate.

    **Fix:** Wait and re-test before concluding the setting is broken.
  </Accordion>
</AccordionGroup>

## Important Notes

* **Create both prerequisite secrets before installing.** A missing one wedges the deployment with no log output; `cpln workload get-deployments` is the one command that names it.
* **The template creates no secrets.** Both credentials are yours, so `helm uninstall` leaves them in place.
* **Clients must use path-style addressing**; virtual-host style is not served.
* **Only the S3 API is publicly routable.** `publicAccess` exposes port `8333` alone — reach the admin UI through `cpln port-forward` or from inside the GVC.
* **Single replica by design** — every redeploy or upgrade is a full S3 outage until the replica is back.
* **Rotating a secret requires a forced redeployment** to take effect; update every consumer at the same time.
* **Uninstall deletes the volume set** and every stored object, keeping a final snapshot for 7 days.
* **Upgrading from `1.0.0` requires the admin credentials secret** and removing `adminUI.username` / `adminUI.password` from your values.

## External References

<CardGroup cols={2}>
  <Card title="SeaweedFS on GitHub" icon="github" href="https://github.com/seaweedfs/seaweedfs">
    Source, releases, and issue tracker
  </Card>

  <Card title="Quick Start with weed mini" icon="rocket" href="https://github.com/seaweedfs/seaweedfs/wiki/Quick-Start-with-weed-mini">
    The all-in-one mode this template runs
  </Card>

  <Card title="Amazon S3 API Support" icon="cloud" href="https://github.com/seaweedfs/seaweedfs/wiki/Amazon-S3-API">
    Which S3 operations SeaweedFS implements
  </Card>

  <Card title="Admin UI" icon="gauge" href="https://github.com/seaweedfs/seaweedfs/wiki/Admin-UI">
    Cluster status, bucket browser, and maintenance
  </Card>

  <Card title="SeaweedFS Template" icon="github" href="https://github.com/controlplane-com/templates/tree/main/seaweedfs">
    View the source files, default values, and chart definition
  </Card>
</CardGroup>
