> ## 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.

# Docmost

> Deploy Docmost on Control Plane using the Template Catalog. Open-source knowledge base and wiki with real-time collaborative editing, spaces, and permissions, backed by PostgreSQL and Redis with local or S3 attachment storage.

## Overview

Docmost is an open-source knowledge base and wiki — a Confluence or Notion alternative with real-time collaborative editing, spaces, and per-space permissions. This template deploys the Docmost server — UI, REST API, and collaboration websockets on port `3000` — backed by a PostgreSQL instance for documents and a bundled Redis for queues and realtime coordination, with attachments stored either on a persistent volume or in an S3 bucket.

### Architecture

* **Docmost server** — A stateful workload running the single official image, which serves the web UI, the REST API, and the Yjs collaboration websocket on port `3000`. It runs its database migrations automatically on boot and scales horizontally through `docmost.replicas`.
* **PostgreSQL** — The [postgres](/template-catalog/templates/postgres) template as a subchart. It holds every page, user, space, and permission.
* **Redis** — A bundled single-node Redis with AOF persistence, deployed by this chart. It carries the background job queues, the socket.io adapter, and cross-replica collaboration sync. Docmost requires it: the server does not report healthy without a reachable Redis.
* **Attachment storage** — Either a persistent volume mounted at `/app/data/storage` (default) or an S3 bucket you own.

### What Gets Created

* **Stateful Docmost Workload** — The Docmost server on port `3000` (`{release}-docmost`), with `docmost.replicas` replicas and configurable CPU and memory.
* **Stateful Redis Workload** — The bundled single-node Redis (`{release}-docmost-redis`) with password authentication.
* **Stateful PostgreSQL Workload** — One PostgreSQL instance from the subchart, with its own volume set.
* **Volume Sets** — One for local attachments (mounted only when `storage.type` is `local`), one for the Redis AOF file, and one for the PostgreSQL data directory.
* **Secret** — A template-created dictionary secret (`{release}-docmost-creds`) holding the PostgreSQL and Redis credentials used to assemble `DATABASE_URL` and `REDIS_URL`. Your prerequisite `APP_SECRET` secret is *not* created by the template — see [Prerequisites](#prerequisites).
* **Identity & Policy** — An identity bound to the Docmost workload, and a policy granting it `reveal` on exactly the secrets it reads: the credentials secret, your prerequisite secret, and — when configured — the S3 key and SMTP secrets. In keyless S3 mode the identity also carries the cloud account link and your bucket-scoped IAM policy.

<Note>
  This template does not create a GVC. You must deploy it into an existing GVC.
</Note>

## Prerequisites

Docmost signs its sessions, invitation links, and share links with an `APP_SECRET` that you supply through an [opaque secret](/guides/create-secret/opaque) created **before** installing. The value is never passed through Helm values.

<Steps>
  <Step title="Create the APP_SECRET secret">
    The payload is a single random string of at least 32 characters:

    ```bash theme={null}
    printf '%s' "$(openssl rand -hex 32)" | \
      cpln secret create-opaque --name my-docmost-app-secret --encoding plain -f -
    ```

    Use `printf` rather than `echo` so the payload carries no trailing newline.
  </Step>

  <Step title="Point the template at it">
    Set `secrets.name` to the secret's name (default `my-docmost-app-secret`). Secrets are org-level, so no GVC flag is involved.
  </Step>

  <Step title="Back the value up">
    Store a copy somewhere safe, outside Control Plane.
  </Step>
</Steps>

<Warning>
  Create the secret **before** installing. Installing without it succeeds, but the deployment then pauses with `The secret my-docmost-app-secret no longer exists. Workload updates are paused until the secret is added or the reference to the secret removed.` and never becomes ready. `APP_SECRET` is also write-once: rotating it logs out every user and invalidates every outstanding invitation and share link. Stored documents are unaffected.
</Warning>

Everything else works with the defaults. Two optional features need their own setup first:

* **S3 attachment storage** — an existing bucket plus either a Control Plane cloud account (AWS) or a static-key secret (S3-compatible servers). See [Attachment Storage](#attachment-storage). Required if you plan to run more than one replica.
* **Authenticated SMTP** — a dictionary secret holding `SMTP_USERNAME` and `SMTP_PASSWORD`. See [Email](#email).

Once your `APP_SECRET` secret exists, install the template using your preferred method:

<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>

## First Run

Docmost ships no default account, and the first person to reach the instance creates the workspace and its owner account.

<Steps>
  <Step title="Wait for the workload to report ready">
    PostgreSQL and Redis come up first, then Docmost applies its schema migrations before it starts serving. A default install typically reaches ready in about a minute.
  </Step>

  <Step title="Open the endpoint and claim the workspace">
    Browse to the canonical `*.cpln.app` endpoint of the `{release}-docmost` workload and complete the setup form to create the workspace and the admin account. Do this as soon as the workload is ready.
  </Step>

  <Step title="Configure email before inviting anyone">
    Member invitations are delivered only by email. Configure [SMTP](#email) before you invite your team.
  </Step>
</Steps>

## Configuration

The default `values.yaml` for this template:

```yaml theme={null}
docmost:
  image: docmost/docmost:0.95.0
  replicas: 1 # >1 REQUIRES storage.type: s3; replicas coordinate via Redis
  appUrl: "" # public base URL used in links; empty = derive from the canonical endpoint
  resources:
    minCpu: 250m
    maxCpu: 1000m
    minMemory: 512Mi
    maxMemory: 2Gi # headroom for boot migrations and large imports

secrets:
  name: my-docmost-app-secret # opaque secret holding APP_SECRET — MUST exist before install

storage:
  type: local # local | s3 (s3 required for replicas > 1)
  local:
    volumeset:
      capacity: 10 # GiB (minimum 10); mounted at /app/data/storage
  s3:
    bucket: my-docmost-bucket
    region: us-east-1
    endpoint: "" # set for S3-compatible servers (e.g. http://my-minio:9000)
    forcePathStyle: false # true for most S3-compatible servers (MinIO)
    cloudAccountName: my-s3-cloud-account # keyless AWS auth; used only when auth.secretName is empty
    policyName: my-docmost-s3-policy # your pre-created bucket-scoped IAM policy
    auth:
      secretName: "" # dictionary secret with AWS_S3_ACCESS_KEY_ID + AWS_S3_SECRET_ACCESS_KEY; S3-compatible servers only
  fileUploadSizeLimit: 50mb # max single attachment size

smtp:
  enabled: false # off = member invites cannot be delivered
  host: smtp.example.com
  port: 587
  secure: false # false = STARTTLS/plain (587); true = implicit TLS (465)
  fromAddress: no-reply@example.com
  fromName: Docmost
  auth:
    secretName: "" # optional dictionary secret with SMTP_USERNAME + SMTP_PASSWORD

publicAccess:
  enabled: true # HTTPS UI + websockets on the auto *.cpln.app endpoint; false = internal-only

internalAccess:
  type: same-gvc # options: none, same-gvc, same-org, workload-list
  workloads: [] # used only with workload-list, e.g. //gvc/GVC_NAME/workload/WORKLOAD_NAME

postgres: # documents, users, spaces
  image: postgres:18
  config:
    username: docmost
    password: change-me-docmost-pg # change before installing
    database: docmost
  resources:
    minCpu: 250m
    maxCpu: 1000m
    minMemory: 512Mi
    maxMemory: 1Gi
  volumeset:
    capacity: 10 # initial capacity in GiB (minimum is 10)

redis: # bundled single node — queues + realtime coordination (required)
  image: redis:8
  auth:
    password: change-me-docmost-redis # change before installing
  resources:
    minCpu: 100m
    maxCpu: 400m
    minMemory: 256Mi
    maxMemory: 512Mi
  volumeset:
    capacity: 10 # GiB (minimum 10); AOF at /data
```

### Docmost Server

* `image` — The official Docmost image. Pin a released tag.
* `replicas` — Number of Docmost replicas. Anything above `1` requires `storage.type: s3`; the chart fails the render with a clear message otherwise. See [Scaling](#scaling).
* `appUrl` — The public base URL Docmost puts in invitation and share links, including the `https://` scheme. Leave it empty to derive it from the workload's canonical endpoint; set it when you serve Docmost on a custom domain.
* `resources` — CPU and memory bounds for the Docmost container. The memory ceiling leaves headroom for the boot migrations and large imports.

### Attachment Storage

`storage.type` decides where uploaded files, images, and avatars are kept.

<Tabs>
  <Tab title="Local volume (default)">
    Attachments are written to a persistent volume set mounted at `/app/data/storage`, and survive restarts, redeploys, and upgrades under the same release name.

    ```yaml theme={null}
    storage:
      type: local
      local:
        volumeset:
          capacity: 10 # initial capacity in GiB (minimum is 10)
    ```

    Local storage works only with a single replica — each replica would get its own volume, so attachments uploaded through one replica would be missing from the others.
  </Tab>

  <Tab title="AWS S3 (keyless)">
    AWS S3 uses a Control Plane cloud identity: no credentials are stored, and the workload's identity vends temporary credentials at runtime. This is the only supported way to reach AWS S3 — the chart rejects static keys unless `storage.s3.endpoint` is set.

    <Steps>
      <Step title="Create a bucket">
        Create an S3 bucket. Set `storage.s3.bucket` and `storage.s3.region` to match.
      </Step>

      <Step title="Set up a Cloud Account">
        If you do not have one, [create a Cloud Account](https://docs.controlplane.com/guides/create-cloud-account) for your AWS account. Set `storage.s3.cloudAccountName` to its name.
      </Step>

      <Step title="Create a bucket-scoped IAM policy">
        Create an AWS IAM policy with the JSON below (replace `YOUR_BUCKET_NAME` with your bucket), then set `storage.s3.policyName` to the policy's name:

        ```json theme={null}
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject",
                "s3:ListBucket"
              ],
              "Resource": [
                "arn:aws:s3:::YOUR_BUCKET_NAME",
                "arn:aws:s3:::YOUR_BUCKET_NAME/*"
              ]
            }
          ]
        }
        ```
      </Step>

      <Step title="Leave the key secret empty">
        Keep `storage.s3.auth.secretName` empty. The template then attaches the cloud account and your policy to the Docmost identity, and no access keys exist anywhere.
      </Step>
    </Steps>
  </Tab>

  <Tab title="S3-compatible (MinIO and others)">
    S3-compatible servers cannot federate with a cloud account, so they use static access keys held in a dictionary secret.

    <Steps>
      <Step title="Create a bucket">
        Create the bucket on your server. For the [MinIO](/template-catalog/templates/minio) template deployed in the same GVC, the S3 API address is `http://WORKLOAD_NAME.GVC_NAME.cpln.local:9000`.
      </Step>

      <Step title="Set the endpoint">
        Set `storage.s3.endpoint` to the S3 API address including scheme and port, and `storage.s3.forcePathStyle: true` (required by most S3-compatible servers).
      </Step>

      <Step title="Create the key secret">
        Create a dictionary secret with the server's credentials and set `storage.s3.auth.secretName` to its name:

        ```bash theme={null}
        cpln secret create-dictionary --name my-docmost-s3-keys \
          --entry AWS_S3_ACCESS_KEY_ID=YOUR_ACCESS_KEY \
          --entry AWS_S3_SECRET_ACCESS_KEY=YOUR_SECRET_KEY
        ```

        The Docmost identity is granted `reveal` on exactly this secret.
      </Step>
    </Steps>
  </Tab>
</Tabs>

`storage.fileUploadSizeLimit` caps the size of a single attachment (default `50mb`).

<Warning>
  An upload larger than `storage.fileUploadSizeLimit` is stored truncated at the limit rather than rejected with an error — the file appears in the page but is incomplete. Raise the limit before importing large attachments.
</Warning>

### Scaling

`docmost.replicas` sets how many Docmost replicas run. Replicas coordinate through the bundled Redis, which carries both the job queues and the collaborative-editing updates, so a document edited on one replica appears live for a reader connected to another.

```yaml theme={null}
docmost:
  replicas: 2
storage:
  type: s3
  s3:
    bucket: my-docmost-bucket
    region: us-east-1
    cloudAccountName: my-s3-cloud-account
    policyName: my-docmost-s3-policy
```

<Warning>
  `docmost.replicas` above `1` requires `storage.type: s3`. Local attachments live on per-replica volumes, so an attachment uploaded through one replica would return a `404` from another. The chart refuses to render the combination.
</Warning>

At two replicas, a rolling upgrade served every health check without a single failure, and killing a replica outright cost one failed request before the replacement rejoined automatically.

### Access

* `publicAccess.enabled` — Serve the UI, API, and collaboration websockets on the auto-assigned `*.cpln.app` HTTPS endpoint (default). Set to `false` for an internal-only instance: external requests are then refused at the edge, and in-GVC callers still reach it per `internalAccess`.
* `internalAccess.type` — Controls which workloads can reach Docmost over the internal network:

| 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.              |
| `workload-list` | Allow access only from workloads listed in `internalAccess.workloads`. |

<Note>
  Flipping `publicAccess.enabled` changes the load balancer configuration, so the public endpoint may return `503` for a minute or two after the upgrade reports success.
</Note>

### Email

SMTP is **off by default**, and a default install works without it — the first visitor still creates the workspace and the admin account, and that account signs in with its password.

```yaml theme={null}
smtp:
  enabled: true
  host: smtp.example.com
  port: 587
  secure: false
  fromAddress: no-reply@example.com
  fromName: Docmost
  auth:
    secretName: my-docmost-smtp # dictionary secret with SMTP_USERNAME + SMTP_PASSWORD
```

For an authenticated relay, create the credentials secret before installing and reference it by name:

```bash theme={null}
cpln secret create-dictionary --name my-docmost-smtp \
  --entry SMTP_USERNAME=apikey --entry SMTP_PASSWORD=YOUR_PASSWORD
```

Leave `smtp.auth.secretName` empty for a relay that does not require authentication. When it is set, the Docmost identity is granted `reveal` on exactly that secret.

<Warning>
  With SMTP disabled there is no way to deliver a member invitation. The image sends no mail and writes no invitation link to the workload logs, so an invitation created in the UI never reaches its recipient. Configure `smtp.*` for any workspace with more than one member.
</Warning>

### Databases

* `postgres.config.username` / `password` / `database` — Credentials for the bundled PostgreSQL from the [postgres](/template-catalog/templates/postgres) subchart. **Change the password before installing** — it seeds the database on first boot and is not updated by later value edits.
* `postgres.resources` / `postgres.volumeset.capacity` — CPU and memory bounds and the initial data volume size in GiB (minimum 10).
* `redis.auth.password` — Password for the bundled Redis, wired into Docmost's `REDIS_URL`. **Change it before installing.** Redis requires authentication: an unauthenticated client is refused.
* `redis.resources` / `redis.volumeset.capacity` — CPU and memory bounds and the volume size for the Redis AOF file at `/data`.

<Note>
  Redis is not optional. Docmost's health check reports the Redis connection alongside the database one, and the workload does not become ready without it. A Redis restart briefly fails readiness on the Docmost workload, which recovers on its own without restarting the application container.
</Note>

## Connecting

| What                    | Value                                                                                                                              |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Public UI / API         | `https://<canonical>.cpln.app` — `status.canonicalEndpoint` of `{release}-docmost` (`cpln workload get {release}-docmost -o yaml`) |
| Collaboration websocket | `wss://<canonical>.cpln.app/collab` — used by the editor, no separate configuration                                                |
| Health check            | `https://<canonical>.cpln.app/api/health` (readiness) and `/api/health/live` (liveness) — unauthenticated                          |
| Internal (same GVC)     | `http://{release}-docmost.{gvc}.cpln.local:3000`                                                                                   |
| Login                   | The admin account created on first visit                                                                                           |
| PostgreSQL (internal)   | `{release}-postgres.{gvc}.cpln.local:5432`, credentials from `postgres.config`                                                     |
| Redis (internal)        | `{release}-docmost-redis.{gvc}.cpln.local:6379`, password from `redis.auth.password`                                               |

## Important Notes

* **Create the `APP_SECRET` secret before installing** — `secrets.name` must point at an existing opaque secret (plain encoding) whose payload is a random string of at least 32 characters. A missing secret pauses the deployment until the secret exists, and the install looks broken until then.
* **`APP_SECRET` is write-once** — rotating it logs out every user and invalidates outstanding invitation and share links. Stored documents are unaffected. Back the value up outside Control Plane.
* **Claim the workspace right after install** — the first visitor to complete the setup form becomes the workspace owner.
* **With SMTP off, member invitations cannot be delivered** — no mail is sent and no invitation link is written to the logs. Configure `smtp.*` before inviting anyone.
* **`docmost.replicas` above `1` requires `storage.type: s3`** — local attachments are per-replica and would `404` across replicas.
* **AWS S3 is keyless only** — use a cloud account plus a bucket-scoped IAM policy. Static keys are accepted only when `storage.s3.endpoint` points at an S3-compatible server.
* **Uploads larger than `storage.fileUploadSizeLimit` are truncated, not rejected** — raise the limit before importing large attachments.
* **Change `postgres.config.password` and `redis.auth.password` before installing** — both seed their component on first boot and are not updated by later value edits.
* **Pages survive restarts and upgrades** — documents live in the PostgreSQL volume set and local attachments in the storage volume set. Uninstalling deletes those volume sets and everything in them; your `APP_SECRET` secret is yours and survives an uninstall.
* **Docmost is licensed under the AGPL** — this template deploys the open-source edition.

## External References

<CardGroup cols={2}>
  <Card title="Docmost Documentation" icon="book" href="https://docmost.com/docs/">
    Official Docmost documentation
  </Card>

  <Card title="Environment Variables" icon="gear" href="https://docmost.com/docs/self-hosting/environment-variables">
    Every setting the Docmost server reads from its environment
  </Card>

  <Card title="File Storage" icon="box" href="https://docmost.com/docs/self-hosting/configuration#file-storage">
    Local and S3 attachment storage options
  </Card>

  <Card title="Email Configuration" icon="envelope" href="https://docmost.com/docs/self-hosting/configuration#email">
    SMTP settings for invitations and notifications
  </Card>

  <Card title="Docmost on GitHub" icon="github" href="https://github.com/docmost/docmost">
    Source code and release notes
  </Card>

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