Keep data in host folders instead of Docker named volumes
The database and files now live under ./data (DATA_DIR to move them), so they can be seen, backed up and restored without going through Docker. tools/volumes-to-folders.sh copies an existing install's named volumes across; it only copies, leaving the volumes in place. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
d5fa67d9e7
commit
985b81d1de
3 changed files with 78 additions and 12 deletions
18
README.md
18
README.md
|
|
@ -57,6 +57,24 @@ A self-hosted web app for organizing your 3D-printing files (STL, 3MF, OBJ, FBX,
|
|||
|
||||
Run the unit tests with `npm test`.
|
||||
|
||||
### Where your data lives
|
||||
|
||||
With Docker, everything sits in folders next to `docker-compose.yml` (set `DATA_DIR` to put them elsewhere):
|
||||
|
||||
| Folder | Holds |
|
||||
|---|---|
|
||||
| `data/pgdata` | The database: models, tags, collections, watches, spools, printers, settings (tokens and printer access codes live here) |
|
||||
| `data/uploads` | Your model files, as uploaded or downloaded |
|
||||
| `data/thumbnails` | Generated pictures, including plate images |
|
||||
| `data/glb` | Converted 3D previews for the viewer |
|
||||
| `data/extracted` | Files unpacked from archives — a cache |
|
||||
|
||||
A backup needs `data/pgdata` (or a `pg_dump`) and `data/uploads`; the other three rebuild themselves. Everything else in
|
||||
the container is replaced on each update, so nothing outside these folders is kept.
|
||||
|
||||
Upgrading from a version that used Docker named volumes: stop the stack, run `tools/volumes-to-folders.sh` to copy the
|
||||
data across, then start it again. The script only copies — the old volumes stay until you remove them yourself.
|
||||
|
||||
## Configuration
|
||||
|
||||
Settings live in the database (editable in the app's **Settings** menu) and fall back to environment variables:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ services:
|
|||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-printhive}
|
||||
POSTGRES_DB: printhive
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
- ${DATA_DIR:-./data}/pgdata:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U printhive -d printhive"]
|
||||
interval: 5s
|
||||
|
|
@ -33,10 +33,12 @@ services:
|
|||
MAKERWORLD_TOKEN: ${MAKERWORLD_TOKEN:-}
|
||||
PRINTABLES_TOKEN: ${PRINTABLES_TOKEN:-}
|
||||
volumes:
|
||||
- uploads:/app/uploads
|
||||
- thumbnails:/app/thumbnails
|
||||
- glb:/app/glb
|
||||
- extracted:/app/extracted # cache of files unpacked from archives
|
||||
# Folders on the host, under ./data (set DATA_DIR to put them elsewhere).
|
||||
# uploads and pgdata hold everything that matters; the other two rebuild.
|
||||
- ${DATA_DIR:-./data}/uploads:/app/uploads
|
||||
- ${DATA_DIR:-./data}/thumbnails:/app/thumbnails
|
||||
- ${DATA_DIR:-./data}/glb:/app/glb
|
||||
- ${DATA_DIR:-./data}/extracted:/app/extracted # cache of files unpacked from archives
|
||||
# No host port published — Traefik auto-discovers the app on the shared network.
|
||||
networks:
|
||||
- default # talk to the db
|
||||
|
|
@ -52,13 +54,6 @@ services:
|
|||
- "traefik.http.routers.printhive.tls.certresolver=${TRAEFIK_CERTRESOLVER:-le}"
|
||||
- "traefik.http.services.printhive.loadbalancer.server.port=3000"
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
uploads:
|
||||
thumbnails:
|
||||
glb:
|
||||
extracted:
|
||||
|
||||
networks:
|
||||
default:
|
||||
# The existing Traefik network (the `proxy` network Traefik runs on).
|
||||
|
|
|
|||
53
tools/volumes-to-folders.sh
Executable file
53
tools/volumes-to-folders.sh
Executable file
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copies Printhive's data out of Docker's named volumes into ./data, for the
|
||||
# switch to host folders. Run it on the server, from the folder holding
|
||||
# docker-compose.yml, with the stack stopped.
|
||||
#
|
||||
# It only copies: the named volumes are left exactly as they were, so if
|
||||
# anything looks wrong you can put the old docker-compose.yml back and start
|
||||
# again with nothing lost.
|
||||
set -euo pipefail
|
||||
|
||||
DATA_DIR="${DATA_DIR:-./data}"
|
||||
VOLUMES=(pgdata uploads thumbnails glb extracted)
|
||||
|
||||
# Compose names volumes "<project>_<name>", the project being this folder's name
|
||||
# unless it's set elsewhere.
|
||||
PROJECT="${COMPOSE_PROJECT_NAME:-$(basename "$(pwd)")}"
|
||||
|
||||
if docker compose ps --status running --quiet 2>/dev/null | grep -q .; then
|
||||
echo "Printhive is still running. Stop it first: docker compose down" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Project: $PROJECT"
|
||||
echo "Copying into: $DATA_DIR"
|
||||
echo
|
||||
|
||||
for name in "${VOLUMES[@]}"; do
|
||||
volume="${PROJECT}_${name}"
|
||||
target="$DATA_DIR/$name"
|
||||
|
||||
if ! docker volume inspect "$volume" >/dev/null 2>&1; then
|
||||
echo "· $volume — no such volume, skipping (nothing to copy)"
|
||||
mkdir -p "$target"
|
||||
continue
|
||||
fi
|
||||
if [ -d "$target" ] && [ -n "$(ls -A "$target" 2>/dev/null)" ]; then
|
||||
echo "· $target already has files in it — skipping, so nothing is overwritten" >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
mkdir -p "$target"
|
||||
# -a keeps ownership and timestamps, which Postgres's data folder needs.
|
||||
docker run --rm \
|
||||
-v "$volume":/from:ro \
|
||||
-v "$(cd "$(dirname "$target")" && pwd)/$(basename "$target")":/to \
|
||||
alpine sh -c 'cp -a /from/. /to/'
|
||||
echo "· $volume → $target ($(du -sh "$target" | cut -f1))"
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Done. Start it up again with: docker compose up -d"
|
||||
echo "Once you're happy, the old volumes can be removed with:"
|
||||
for name in "${VOLUMES[@]}"; do echo " docker volume rm ${PROJECT}_${name}"; done
|
||||
Loading…
Reference in a new issue