# Docker Volume Forensics: Tracing Lost Data — 2026-07-14

## Context

User believed years of HomeAssistant InfluxDB data existed on Docker
host 10.0.30.100. Initial scan found only a 15 KB skeleton volume. A
deep forensic search was needed to locate the actual data.

## Search Methodology

When looking for "lost" Docker container data, check ALL of these —
not just named volumes:

### 1. Named Docker Volumes

```bash
# List all volumes (anonymous ones have hash names)
docker volume ls -q | wc -l  # 121 volumes found

# Search all volume mountpoints for specific data signatures
for d in /var/lib/docker/volumes/*/; do
  if ls "${d}_data/" 2>/dev/null | grep -q "^data$\|^meta$\|^wal$\|^influxd.bolt$\|^engine$"; then
    echo "FOUND: $d"
    ls -la "${d}_data/"
  fi
done
```

### 2. Bind Mounts (Host Filesystem)

Containers may use bind mounts instead of named volumes. Check:
```bash
# Directories on root partition
ls -la /var/lib/influxdb/ /var/lib/influxdb2/ 2>/dev/null

# ZFS pools (if Docker is on ZFS)
find /pool01_n2_redundant -maxdepth 5 -name "*.tsm" 2>/dev/null  # InfluxDB v1
find /pool01_n2_redundant -maxdepth 5 -name "influxd.bolt" 2>/dev/null  # InfluxDB v2

# Home directories (common bind mount source)
ls /pool01_n2_redundant/homes/dominik/ | grep -i influx
```

### 3. Docker Config Files Outside Volumes

```bash
# InfluxDB config was at /var/lib/docker/influx_cfg/influxdb.conf
# (NOT inside a volume — a bind-mounted config directory)
find /var/lib/docker -maxdepth 3 -name "*.conf" -o -name "*.yml" 2>/dev/null | grep -i influx
```

### 4. Portainer Stack Definitions

Portainer stores compose stack definitions in TWO locations:

**Location 1:** `/data/compose/` (sparse, older stacks — may have empty dirs)
**Location 2:** `/var/lib/docker/volumes/portainer_data/_data/compose/` (canonical)

Each stack directory contains versioned subdirectories (`v1/`, `v2/`, ...)
with `docker-compose.yml`. To read the latest version of every stack:

```bash
for d in /var/lib/docker/volumes/portainer_data/_data/compose/*/; do
  sid=$(basename $d)
  latest=$(ls -d ${d}v*/ 2>/dev/null | sort -V | tail -1)
  if [ -n "$latest" ]; then
    echo "=== STACK $sid ($(basename $latest)) ==="
    cat "${latest}"* 2>/dev/null
    echo ""
  fi
done
```

Portainer DB (BoltDB) is at:
`/var/lib/docker/volumes/portainer_data/_data/portainer.db`

Can also extract stack defs via `strings`:
```bash
strings /var/lib/docker/volumes/portainer_data/_data/portainer.db | grep -i influx
```

Portainer backups at `/var/lib/docker/volumes/portainer_data/_data/backup/`
contain Portainer config + compose files but NOT Docker volume data.

### 4b. Real-World Portainer Stack Discovery (10.0.30.100, 2026-07-14)

Found 9 stacks in canonical location: 1 (Traefik), 7 (Seafile), 10
(Mailserver), 13 (Seafile-old), 14 (Monitoring: InfluxDB+Grafana+Loki),
18 (Imkerei/Ghost blog), 19 (GitLab), 26 (Mosquitto+HomeAssistant),
56 (Spoolman).

Stack 14 contained the InfluxDB service definition:
- `InfluxSrv` container with `influxdb:latest`
- Data volume `506f355...` mounted at `/var/lib/influxdb2` — **deleted**
  (not in `docker volume ls`, not on disk)
- Config volume `c2f27e78...` mounted at `/etc/influxdb2` — exists but empty
- Bind mounts: `/var/lib/docker/influx_cfg/influxdb.conf` (v1 config from
  März 2017) and `types.db` (collectd types)
- Grafana env showed `INFLUXDB_NAME: cadvisor` — infrastructure monitoring,
  NOT Home Assistant data

Conclusion: The InfluxDB v1 data was deleted with the container. Only the
Docker image (376 MB) and config artifacts survived.

### 5. Shell History (Last Resort)

Previous Docker commands may reveal the container's mount config:
```bash
# Check shell history files on ZFS home directories
cat /pool01_n2_redundant/homes/dominik/.influx_history
cat /pool01_n2_redundant/homes/dominik/.docker_history 2>/dev/null
cat /pool01_n2_redundant/homes/dominik/.bash_history 2>/dev/null | grep influx
```

### 6. Docker Image Inventory

Old images indicate what services ran, even if containers are gone:
```bash
docker images --format "{{.Repository}}:{{.Tag}}\t{{.Size}}" | grep -i influx
# influxdb:latest   376MB   (2 years ago)
# telegraf:latest   374MB
```

## Key Findings (10.0.30.100, 2026-07-14)

| What | Where | Size | Status |
|------|-------|------|--------|
| InfluxDB v1 data | Anonymous volume `d735775...` | 15 KB | Skeleton only — `influxd.bolt` + `influxd.sqlite` from Jan 2023 |
| InfluxDB v1 config | `/var/lib/docker/influx_cfg/influxdb.conf` | — | v1-style config (meta/data/wal dirs) |
| Telegraf config | Named volume `telegraf` | 246 KB | Config only, no data |
| Shell history | `/pool01_n2_redundant/homes/dominik/.influx_history` | 1 KB | Shows `collectd` + `cadvisor` databases (2017-era) |
| Docker image | `influxdb:latest` | 376 MB | Cached, 2 years old |
| `.tsm` files | Nowhere | — | No InfluxDB v1 data files found on any filesystem |

**Conclusion:** The InfluxDB v1 container was removed years ago. Its
data volume contained only the bolt/sqlite skeleton (metadata without
actual time-series data). The real v1 data (`*.tsm` files) was deleted
with the container. Only the Docker image and config artifacts remained.

## Pitfall: "Data Must Be Here" vs. "Data IS Gone"

When a user insists data existed on a host, systematically check all
storage locations. BUT — if after exhaustive search no data files are
found (no `.tsm`, no `data/` directory with shards, no large volumes),
the data was likely deleted when the container was removed. Docker
container removal (`docker rm`) does NOT delete volumes by default, but
`docker rm -v` or `docker run --rm` DOES delete anonymous volumes.

## Pitfall: `du` Hangs on ZFS-Backed Docker Volumes

When checking Docker volume sizes on a ZFS-backed Docker installation,
`du -sh /var/lib/docker/volumes/*/_data` can hang indefinitely (D-state)
due to ZFS metadata I/O. Use:

```bash
# Quick ls-based check (doesn't read file contents)
ls -la /var/lib/docker/volumes/VOLNAME/_data/

# ZFS-native size (instant, no file traversal)
zfs list -o refer pool/docker/VOLNAME 2>/dev/null

# Per-volume with timeout (5s max per volume)
for vol in $(docker volume ls -q); do
  timeout 5 du -sh /var/lib/docker/volumes/$vol/_data 2>/dev/null || echo "$vol: TIMEOUT"
done
```
