Backup and Recovery
Back up Clouisle metadata, indexes, queues, and upload assets
A recoverable Clouisle backup includes at least PostgreSQL, Qdrant, upload storage, and deployment configuration/Secrets. Redis mainly holds queues, caches, and session state; whether to back it up depends on the organization's recovery requirements for unfinished tasks.
What to Back Up
| Content | Role | Recommendation |
|---|---|---|
| PostgreSQL | Users, teams, resources, configuration, audit, document metadata | Daily full backup + continuous archiving |
| Qdrant | Document vectors | Coordinate with database backups |
| Upload storage | Raw documents, attachments, media, and sandbox artifacts | Keep at the same point in time as PostgreSQL metadata |
.env/Kubernetes Secret | Connection and signing configuration | Store encrypted; never commit to the repository |
| Redis | Celery/cache/session | Decide according to task recovery requirements |
Backup commands
This is an online, best-effort multi-store backup, not a transactionally consistent snapshot; schedule downtime separately when strict cross-store consistency is required. Run these commands from the directory containing docker-compose.yml and .env; -T is required for cron/non-interactive runs.
# Stop the writers first to reduce write volume during the backup
docker compose stop worker sandbox-worker beat
# PostgreSQL: custom-format dump
docker compose exec -T db pg_dump -U "${POSTGRES_USER:-postgres}" -Fc "${POSTGRES_DB:-clouisle}" \
> "postgres_$(date +%Y%m%d_%H%M%S).dump"
# Redis (optional; authenticates when REDIS_PASSWORD is set)
docker compose exec -T redis sh -c 'redis-cli ${REDIS_PASSWORD:+-a "$REDIS_PASSWORD"} --rdb -' \
> "redis_$(date +%Y%m%d_%H%M%S).rdb"
# Uploads: mounted only on api, so stream the volume contents directly
docker compose exec -T api tar -czf - -C /app uploads \
> "uploads_$(date +%Y%m%d_%H%M%S).tar.gz"
# Start the workers again once the backup finishes
docker compose start worker sandbox-worker beatQdrant needs a snapshot created and downloaded for every existing collection. The supplied image is qdrant/qdrant:v1.18.3; recovery must use a version with compatible snapshot API semantics. When QDRANT_API_KEY is enabled, export it in your shell first (Compose does not inject .env into your shell):
QDRANT_URL="${QDRANT_URL:-http://localhost:6333}"
QDRANT_AUTH=()
[ -n "${QDRANT_API_KEY:-}" ] && QDRANT_AUTH=(--header "api-key: $QDRANT_API_KEY")
# List every collection, put one returned name into COLLECTION, then repeat per collection
curl --fail "${QDRANT_URL}/collections" "${QDRANT_AUTH[@]}"
COLLECTION='replace-with-a-name-returned-above'
curl --fail --request POST "${QDRANT_URL}/collections/${COLLECTION}/snapshots" "${QDRANT_AUTH[@]}"
curl --fail "${QDRANT_URL}/collections/${COLLECTION}/snapshots" "${QDRANT_AUTH[@]}"
SNAPSHOT_NAME='replace-with-the-name-returned-above'
curl --fail "${QDRANT_URL}/collections/${COLLECTION}/snapshots/${SNAPSHOT_NAME}" \
"${QDRANT_AUTH[@]}" --output "${COLLECTION}_${SNAPSHOT_NAME}.snapshot"Knowledge-base vector collections are usually named kb_dim_<dimension> and memory vectors memory_entities_dim_<dimension>. The default http://localhost:6333 only works while the Qdrant host-port mapping is published; if production removed it, temporarily bind 127.0.0.1:6333:6333 or use a network-attached helper, then remove the mapping again.
The Kubernetes equivalents (namespace clouisle):
kubectl -n clouisle exec -i statefulset/postgres -- pg_dump -U postgres -Fc clouisle > postgres.dump
kubectl -n clouisle exec -i deployment/api -- tar -czf - -C /app uploads > uploads.tar.gz
# The Qdrant image may not ship curl: port-forward in one terminal, run the snapshot commands above
# in the other with QDRANT_URL=http://127.0.0.1:6333
kubectl -n clouisle port-forward statefulset/qdrant 6333:6333Restore order and commands
Restore order:
- Restore PostgreSQL and its extensions (
pg_search,pg_stat_statements). - Restore Qdrant collections and uploaded files.
- Restore Secrets and environment variables; confirm
SECRET_KEYstays consistent. - Start the API; confirm health and schema initialization.
- Start Worker, Sandbox Worker, and the single Beat.
- Check document status, vector dimensions, Agents, Workflows, and API Keys.
- Run one Knowledge Base retrieval and one Agent/Workflow smoke request.
Keep application services stopped while restoring PostgreSQL and Qdrant, but leave the database and Qdrant services themselves running; start the application only after both stores are restored.
# Compose
docker compose stop api worker sandbox-worker beat
docker compose exec -T db pg_restore -U "${POSTGRES_USER:-postgres}" -d "${POSTGRES_DB:-clouisle}" \
--clean --if-exists < postgres.dump
# Kubernetes
kubectl -n clouisle scale deployment api worker sandbox-worker beat --replicas=0
kubectl -n clouisle exec -i statefulset/postgres -- pg_restore -U postgres -d clouisle --clean --if-exists < postgres.dumpUpload the snapshot for every backed-up collection (the upload restores the collection; do not send an extra /recover request):
COLLECTION='replace-with-a-backed-up-collection-name'
SNAPSHOT_FILE='replace-with-the-matching-snapshot.snapshot'
curl --fail --request POST \
"${QDRANT_URL}/collections/${COLLECTION}/snapshots/upload" \
"${QDRANT_AUTH[@]}" \
--form "snapshot=@${SNAPSHOT_FILE}"Finally start only the API to restore the uploads archive, then restore each workload's replica count (the supplied manifest defaults to api=2, worker=2, sandbox-worker=1, beat=1):
# Compose
docker compose start api
docker compose exec -T api tar -xzf - -C /app < uploads.tar.gz
docker compose start worker sandbox-worker beat
# Kubernetes
kubectl -n clouisle scale deployment api --replicas=2
kubectl -n clouisle rollout status deployment/api
kubectl -n clouisle exec -i deployment/api -- tar -xzf - -C /app < uploads.tar.gz
kubectl -n clouisle scale deployment worker --replicas=2
kubectl -n clouisle scale deployment sandbox-worker --replicas=1
kubectl -n clouisle scale deployment beat --replicas=1After restoring, verify GET /api/v1/health, login, a Knowledge Base search, one upload, and a representative Workflow. Redis queue state is optional; when it is not restored, re-submit the jobs that were pending at the moment of failure.
Automation
Schedule the Compose commands with host cron, or run equivalent commands in a Kubernetes CronJob that mounts an explicitly provisioned backup destination and reads credentials from a Secret. Keep encrypted, off-site copies, define retention, and run regular restore drills in a scratch environment.
Restoring only PostgreSQL leaves resources missing raw files or vectors; restoring only Qdrant leaves vectors that cannot be linked. Metadata, files, and indexes must be handled at the same recovery point.
How is this guide?