From 064e78247a9746698c12e1ae70a4d8b769eab90e Mon Sep 17 00:00:00 2001 From: David Moore Date: Thu, 15 Oct 2026 15:35:47 +1100 Subject: [PATCH] fix(docker): let the filesystem store write to a mounted volume --- Dockerfile | 4 ++ packages/docs/content/configuration.mdx | 10 ++-- packages/docs/content/deploy-with-docker.mdx | 48 ++++++++++++++++++++ packages/website/public/compose.yml | 8 ++++ 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4beb3bc8..d41fbad0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -58,6 +58,10 @@ ARG SUGABOTS_VERSION SUGABOTS_COMMIT ENV SUGABOTS_VERSION=$SUGABOTS_VERSION \ SUGABOTS_COMMIT=$SUGABOTS_COMMIT +# Where the filesystem blob store keeps files when it is chosen. Made here so a +# volume mounted on it starts out writable by the app's user. +RUN mkdir -p /data/files && chown node:node /data/files + USER node EXPOSE 3000 diff --git a/packages/docs/content/configuration.mdx b/packages/docs/content/configuration.mdx index 93577df9..9a765eb7 100644 --- a/packages/docs/content/configuration.mdx +++ b/packages/docs/content/configuration.mdx @@ -61,11 +61,11 @@ Generate each with `openssl rand -base64 32`. Keep them private and backed up. ## File storage -| Variable | Default | Description | -| ---------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `BLOB_STORE_PROVIDER` | `postgres` | Where files are kept, such as tool results too large to hand a model whole. `postgres` keeps them in the database, up to 100 MiB each. `filesystem` keeps them on disk, for a single server. | -| `BLOB_STORE_FILESYSTEM_ROOT` | Required for `filesystem` | The directory files are kept under, such as a mounted volume. Back it up with the database. | -| `THREAD_FILE_MAX_BYTES` | The blob store's limit | The largest file a thread keeps, in bytes, when lower than what the blob store keeps. | +| Variable | Default | Description | +| ---------------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `BLOB_STORE_PROVIDER` | `postgres` | Where files are kept, such as tool results too large to hand a model whole. `postgres` keeps them in the database, up to 100 MiB each. `filesystem` keeps them on disk, for a single server. Choose before any files are kept: changing it doesn't move them. | +| `BLOB_STORE_FILESYSTEM_ROOT` | Required for `filesystem` | The directory files are kept under, such as a mounted volume. The Docker image has `/data/files` ready for one; [another directory](/docs/deploy-with-docker#files-on-a-volume) must be writable by the user it runs as, uid 1000. Back it up with the database. | +| `THREAD_FILE_MAX_BYTES` | The blob store's limit | The largest file a thread keeps, in bytes, when lower than what the blob store keeps. | ## Web app diff --git a/packages/docs/content/deploy-with-docker.mdx b/packages/docs/content/deploy-with-docker.mdx index 1d136b19..5f559448 100644 --- a/packages/docs/content/deploy-with-docker.mdx +++ b/packages/docs/content/deploy-with-docker.mdx @@ -147,6 +147,46 @@ Either way, the service needs these settings: See the [configuration reference](/docs/configuration) for what each does. +## Files on a volume + +Sugabots keeps files in the database unless told otherwise. There, every file +adds to the database and its backups, and each one is held in memory while it's +saved. To keep them on a volume instead, give the `sugabots` service these +settings and a `files` volume in `compose.yml`: + +```yaml title="compose.yml" +services: + sugabots: + environment: + BLOB_STORE_PROVIDER: filesystem + BLOB_STORE_FILESYSTEM_ROOT: /data/files + volumes: + - files:/data/files + +volumes: + postgres_data: + files: +``` + +Choose before any files are kept. Files already in the database stay there, +and Sugabots can't read them once this changes. + +To keep files in a folder on the server rather than a Docker volume, mount the +folder at `/data/files` instead, and give it to the user Sugabots runs as: + +```sh +sudo mkdir -p /srv/sugabots/files +sudo chown 1000:1000 /srv/sugabots/files +``` + +```yaml title="compose.yml" + volumes: + - /srv/sugabots/files:/data/files +``` + +On a hosting platform, attach a persistent disk at `/data/files` and set the +same two settings. Without one, keep the default. + ## Email Sugabots sends email for invites and address verification. Without a provider, @@ -231,6 +271,14 @@ This deletes whatever is in the database before loading the backup. If the backup is from an older version, Sugabots applies the newer migrations when it starts. +If you keep [files on a volume](#files-on-a-volume), back it up at the same +time as the database, and restore the two together: the database lists the +files, and the volume holds them. To write the volume to a file: + +```sh +docker run --rm -v sugabots_files:/data/files -v "$PWD":/backup alpine tar czf /backup/sugabots-files.tar.gz -C /data/files . +``` + On a hosting platform, use the platform's database backups, and keep a copy of the two keys. diff --git a/packages/website/public/compose.yml b/packages/website/public/compose.yml index e91ed45f..ddc6528f 100644 --- a/packages/website/public/compose.yml +++ b/packages/website/public/compose.yml @@ -15,6 +15,13 @@ services: BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:?Set it in .env} CREDENTIALS_ENCRYPTION_KEY: ${CREDENTIALS_ENCRYPTION_KEY:?Set it in .env} EMAIL_TRANSACTIONAL_FROM: Sugabots + # Files are kept in the database. To keep them on a volume instead, + # uncomment these lines and the two below before any are kept: + # https://sugabots.ai/docs/deploy-with-docker#files-on-a-volume + # BLOB_STORE_PROVIDER: filesystem + # BLOB_STORE_FILESYSTEM_ROOT: /data/files + # volumes: + # - files:/data/files depends_on: postgres: condition: service_healthy @@ -36,3 +43,4 @@ services: volumes: postgres_data: + # files: