From 490cbbb8123f34aa4ccd2b06e4911b1317971666 Mon Sep 17 00:00:00 2001 From: Beda Schmid Date: Mon, 2 Mar 2026 22:11:33 -0300 Subject: [PATCH] Normalize compose host bind mount paths --- .env.example | 3 +++ .gitignore | 1 + AGENTS.md | 2 +- README.md | 21 ++++++++++++++------- doc/operations-and-configuration.md | 21 +++++++++++---------- docker-compose.yml | 24 +++++++++--------------- 6 files changed, 39 insertions(+), 33 deletions(-) diff --git a/.env.example b/.env.example index 9a318b8..15fa051 100644 --- a/.env.example +++ b/.env.example @@ -4,6 +4,9 @@ # Development defaults (HTTP local stack) APP_ENV=development HOST_BIND_IP=127.0.0.1 +# Optional host directory for persistent bind mounts in docker-compose.yml. +# Defaults to ./data when unset. +# DCM_DATA_DIR=./data POSTGRES_USER=dcm POSTGRES_PASSWORD=ChangeMe-Postgres-Secret diff --git a/.gitignore b/.gitignore index 8f185ec..a1b0d86 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ build/ # Data and generated artifacts (runtime only) data/ +typesense-data/ # OS / IDE .DS_Store diff --git a/AGENTS.md b/AGENTS.md index f15a25f..165f9ee 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,7 +3,7 @@ ## Stack Snapshot - DMS monorepo with FastAPI API + RQ worker (`backend/`) and React + Vite + TypeScript frontend (`frontend/`). - Services in `docker-compose.yml`: `api`, `worker`, `frontend`, `db` (Postgres), `redis`, `typesense`. -- Runtime persistence uses Docker named volumes (`db-data`, `redis-data`, `dcm-storage`, `typesense-data`). +- Runtime persistence uses host bind mounts under `${DCM_DATA_DIR:-./data}` (`db-data`, `redis-data`, `storage`, `typesense-data`). ## Project Layout - Backend app code: `backend/app/` (`api/`, `services/`, `db/`, `models/`, `schemas/`, `worker/`). diff --git a/README.md b/README.md index 7474734..28c3ec6 100644 --- a/README.md +++ b/README.md @@ -113,19 +113,26 @@ docker compose logs -f api worker ## Where Your Data Is Stored -LedgerDock stores data in Docker volumes so it survives container restarts: +LedgerDock stores persistent runtime data in host bind mounts. By default the host root is `./data`, or set `DCM_DATA_DIR` to move it: -- `db-data` for PostgreSQL data -- `redis-data` for Redis data -- `dcm-storage` for uploaded files and app storage -- `typesense-data` for the search index +- `${DCM_DATA_DIR:-./data}/db-data` for PostgreSQL data +- `${DCM_DATA_DIR:-./data}/redis-data` for Redis data +- `${DCM_DATA_DIR:-./data}/storage` for uploaded files and app storage +- `${DCM_DATA_DIR:-./data}/typesense-data` for the search index -If you switch to host bind mounts (for example under `./data/`), create those directories and grant write access to container runtime user `uid=10001` for backend storage paths. +Before first run, create storage and grant write access to container runtime user `uid=10001`: + +```bash +mkdir -p ${DCM_DATA_DIR:-./data}/storage +sudo chown -R 10001:10001 ${DCM_DATA_DIR:-./data}/storage +sudo chmod -R u+rwX,g+rwX ${DCM_DATA_DIR:-./data}/storage +``` To remove everything, including data: ```bash -docker compose down -v +docker compose down +rm -rf ${DCM_DATA_DIR:-./data} ``` Warning: this permanently deletes your LedgerDock data on this machine. diff --git a/doc/operations-and-configuration.md b/doc/operations-and-configuration.md index 5b4e883..c826841 100644 --- a/doc/operations-and-configuration.md +++ b/doc/operations-and-configuration.md @@ -10,16 +10,17 @@ - `worker` (RQ worker via `python -m app.worker.run_worker`) - `frontend` (Vite React UI) -Persistent volumes: -- `db-data` -- `redis-data` -- `dcm-storage` -- `typesense-data` +Persistent host bind mounts (default root `./data`, overridable with `DCM_DATA_DIR`): +- `${DCM_DATA_DIR:-./data}/db-data` +- `${DCM_DATA_DIR:-./data}/redis-data` +- `${DCM_DATA_DIR:-./data}/storage` +- `${DCM_DATA_DIR:-./data}/typesense-data` Reset all persisted runtime data: ```bash -docker compose down -v +docker compose down +rm -rf ${DCM_DATA_DIR:-./data} ``` ## Core Commands @@ -44,14 +45,14 @@ docker compose logs -f ## Host Bind Mounts -If you replace Docker named volumes with host bind mounts (for example `./data/storage:/data/storage`), ensure host directories exist and are writable by the backend runtime user. +Compose is configured with host bind mounts for persistent data. Ensure host directories exist and are writable by the backend runtime user. Backend and worker run as non-root user `uid=10001` inside containers. For host-mounted storage paths: ```bash -mkdir -p ./data/storage -sudo chown -R 10001:10001 ./data/storage -sudo chmod -R u+rwX,g+rwX ./data/storage +mkdir -p ${DCM_DATA_DIR:-./data}/storage +sudo chown -R 10001:10001 ${DCM_DATA_DIR:-./data}/storage +sudo chmod -R u+rwX,g+rwX ${DCM_DATA_DIR:-./data}/storage ``` If permissions are incorrect, API startup fails with errors similar to: diff --git a/docker-compose.yml b/docker-compose.yml index 01f8db1..9d14edc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?POSTGRES_PASSWORD must be set} POSTGRES_DB: ${POSTGRES_DB:?POSTGRES_DB must be set} volumes: - - db-data:/var/lib/postgresql/data + - ${DCM_DATA_DIR:-./data}/db-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:?POSTGRES_USER must be set} -d ${POSTGRES_DB:?POSTGRES_DB must be set}"] interval: 10s @@ -25,7 +25,7 @@ services: - "--requirepass" - "${REDIS_PASSWORD:?REDIS_PASSWORD must be set}" volumes: - - redis-data:/data + - ${DCM_DATA_DIR:-./data}/redis-data:/data networks: - internal @@ -36,7 +36,7 @@ services: - "--api-key=${TYPESENSE_API_KEY:?TYPESENSE_API_KEY must be set}" - "--enable-cors" volumes: - - typesense-data:/data + - ${DCM_DATA_DIR:-./data}/typesense-data:/data restart: unless-stopped networks: - internal @@ -76,15 +76,15 @@ services: TYPESENSE_PORT: 8108 TYPESENSE_API_KEY: ${TYPESENSE_API_KEY:?TYPESENSE_API_KEY must be set} TYPESENSE_COLLECTION_NAME: documents -# ports: - # - "${HOST_BIND_IP:-127.0.0.1}:8000:8000" + # ports: + # - "${HOST_BIND_IP:-127.0.0.1}:8000:8000" security_opt: - no-new-privileges:true cap_drop: - ALL volumes: - ./backend/app:/app/app - - dcm-storage:/data + - ${DCM_DATA_DIR:-./data}/storage:/data/storage depends_on: db: condition: service_healthy @@ -124,7 +124,7 @@ services: TYPESENSE_COLLECTION_NAME: documents volumes: - ./backend/app:/app/app - - dcm-storage:/data + - ${DCM_DATA_DIR:-./data}/storage:/data/storage security_opt: - no-new-privileges:true cap_drop: @@ -150,8 +150,8 @@ services: VITE_API_BASE: ${VITE_API_BASE:-} CORS_ORIGINS: '${CORS_ORIGINS:-["http://localhost:5173","http://localhost:3000"]}' VITE_ALLOWED_HOSTS: ${VITE_ALLOWED_HOSTS:-} - # ports: - # - "${HOST_BIND_IP:-127.0.0.1}:5173:5173" + # ports: + # - "${HOST_BIND_IP:-127.0.0.1}:5173:5173" volumes: - ./frontend/src:/app/src - ./frontend/index.html:/app/index.html @@ -169,12 +169,6 @@ services: internal: restart: unless-stopped -volumes: - db-data: - redis-data: - dcm-storage: - typesense-data: - networks: internal: driver: bridge