# Data Persistence & Backup

All tenant data in Synapse-Cortex — **custom asset types**, tickets, users, assets,
playbooks, the credential vault, audit log, everything — lives in **one PostgreSQL
database**. Nothing is stored in the application image or the app container's
filesystem, so it all persists together in a single Docker volume.

## Where the data lives

`docker-compose.yml` mounts the database onto a **named, pinned** volume:

```yaml
volumes:
  cortex_pgdata:
    name: synapse_cortex_pgdata      # explicit, stable name
```

and pins the Compose project name:

```yaml
name: synapse-cortex
```

Pinning both means the database **always resolves to the same physical volume**,
no matter which folder the repo sits in, whether the folder is renamed or copied,
or whether you pass `-p`/`--project-name`. This is deliberate: Docker Compose's
default volume name is `<project>_<volume>`, and the project name defaults to the
**directory name** — so without pinning, renaming or copying the checkout would
silently mount a *fresh, empty* database and every custom asset type (and all
other data) would appear to vanish on the next rebuild.

## What is safe, and what destroys data

| Command | Data survives? |
|---|---|
| `docker compose up --build` | ✅ Yes — rebuilds the image, keeps the volume |
| `docker compose restart` / `stop` / `start` | ✅ Yes |
| `docker compose down` | ✅ Yes — removes containers, **keeps** named volumes |
| `docker compose up -d --force-recreate` | ✅ Yes |
| **`docker compose down -v`** | ❌ **No — deletes the volume and all data** |
| `docker volume rm synapse_cortex_pgdata` | ❌ No |
| `docker system prune --volumes` / `--all --volumes` | ❌ No |

The one rule: **never use `-v` on `down`** (or prune volumes) unless you truly
want to wipe the database back to a fresh install.

## One-time migration for an existing deployment

If you have been running Cortex **before this pinned-name change**, your data is
in the old auto-named volume `synapse-cortexv2_cortex_pgdata` (Compose derived it
from the `Synapse-Cortexv2` folder name). Copy it into the new pinned volume
**once** so the new config picks it up instead of starting empty:

```bash
# stop the stack first so nothing is writing to the DB
docker compose down

# create the new pinned volume and copy the old data into it
docker volume create synapse_cortex_pgdata
docker run --rm \
  -v synapse-cortexv2_cortex_pgdata:/from \
  -v synapse_cortex_pgdata:/to \
  alpine sh -c "cd /from && cp -a . /to"

# bring it back up on the new, stable volume
docker compose up -d --build
```

Verify your custom asset types are present (Admin → Asset Types), then you can
remove the old volume: `docker volume rm synapse-cortexv2_cortex_pgdata`.

> If you were running a **fresh** stack (no data yet), skip this — just
> `docker compose up --build` and set up as normal.

## Backups (recommended)

Even with a pinned volume, keep periodic backups. Two easy options:

**Logical dump (portable, restores into any Postgres):**

```bash
# back up
docker compose exec -T db pg_dump -U cortex synapse_cortex > cortex-backup.sql

# restore (into a running, empty db)
docker compose exec -T db psql -U cortex -d synapse_cortex < cortex-backup.sql
```

**Whole-volume snapshot (exact bytes, fastest):**

```bash
# back up the volume to a tarball in the current directory
docker run --rm -v synapse_cortex_pgdata:/data -v "$PWD":/backup \
  alpine tar czf /backup/cortex_pgdata.tar.gz -C /data .

# restore into a fresh volume
docker run --rm -v synapse_cortex_pgdata:/data -v "$PWD":/backup \
  alpine sh -c "cd /data && tar xzf /backup/cortex_pgdata.tar.gz"
```

Use the credentials from your `.env` if you changed `POSTGRES_USER` /
`POSTGRES_DB` from the defaults (`cortex` / `synapse_cortex`).
