Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@ range. Versions before 0.3.0 predate the GitHub releases and are reconstructed f
Merge commits, automated `Tagged <version>` commits and version bumps are omitted, as are bullets
that only restate the release note above them.

## Unreleased

### Fixed

- **`roll restore --force` works on a stopped environment.** The restore only ran `roll env down`
when one of the environment's containers was running. Docker refuses to remove a volume that any
container references, stopped ones included, so a stopped environment kept every volume in use
and the restore died on the first one. `stopEnvironment` in `utils/backup.sh` now takes the
environment down whenever it has containers, running or stopped, and passes `--remove-orphans`,
so containers of services the current `.env.roll` no longer enables are removed as well; volumes
are kept. `roll restore-full` shares the change.
- **A volume `roll restore --force` cannot remove now stops the restore with a reason.** The
`docker volume rm` result was never checked, so under roll's errexit the restore ended right after
`Removing existing volume: …` with exit 1 and no error message. It now prints Docker's error and
the containers still holding the volume, then exits 1 without writing into it. Volumes restored
earlier in the same run keep their new contents.

## [0.8.5](https://github.com/epartment/rolldev/releases/tag/0.8.5) — 2026-10-07

### Added
Expand Down
15 changes: 15 additions & 0 deletions docs/backup-restore.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,21 @@ roll restore --force
docker volume rm ${ROLL_ENV_NAME}_dbdata
```

**`--force` fails with "Could not remove volume … volume is in use":**

Docker will not remove a volume that a container still references, and a stopped container counts.
`roll restore` runs `roll env down --remove-orphans` first whenever the environment has containers,
running or stopped, so this error means something else still holds the volume: a container from
outside this environment, or an `env down` that failed. The restore stops at that volume, before
writing into it, and names the containers holding it. Volumes restored earlier in the same run keep
their new contents, so rerun the whole restore once the containers are gone:

```bash
# Remove the container by the name the error printed, then retry
docker rm <name>
roll restore --force
Comment on lines +330 to +332
```

**Encrypted backup won't decrypt:**
```bash
# Ensure GPG is installed
Expand Down
36 changes: 29 additions & 7 deletions utils/backup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,23 @@ function restoreVolume() {
if [[ $RESTORE_FORCE -eq 1 ]]; then
logMessage INFO "Removing existing volume: $volume_name"
logVerbose "Executing: docker volume rm $volume_name"
docker volume rm "$volume_name" >/dev/null 2>&1
# Docker refuses to remove a volume that any container references, stopped ones
# included. Unchecked, that failure ended the restore under errexit with no message at
# all, and without errexit the idempotent `docker volume create` below would let the
# archive be extracted over the old contents and report success. Either way a volume
# that could not be removed must never be restored into, and the user needs the reason.
local rm_output
if ! rm_output="$(docker volume rm "$volume_name" 2>&1)"; then
local holders
holders="$(docker ps -a --filter volume="$volume_name" --format '{{.Names}}' 2>/dev/null | tr '\n' ' ')" || true
holders="${holders% }"
logMessage ERROR "Could not remove volume $volume_name: $rm_output"
if [[ -n "$holders" ]]; then
logMessage ERROR "Containers still holding it (running or stopped): $holders"
fi
logMessage ERROR "Remove them first - 'roll env down' for this environment's containers, 'docker rm <name>' for any other - then rerun the restore."
return 1
fi
else
logMessage ERROR "Volume $volume_name already exists. Use --force to overwrite."
return 1
Expand Down Expand Up @@ -754,6 +770,12 @@ function signEnvironmentCertificate() {
## Restore and restore-full disagreed here: a full restore has no .env.roll yet, because the
## configuration is inside the backup, and the env commands require one. The guard covers both,
## so the two callers no longer need separate copies.
##
## Stopped containers count: Docker will not remove a volume that any container references, running
## or not, so a stopped environment left in place made every `restore --force` fail on its first
## volume. `--remove-orphans` takes the containers of services the current .env.roll no longer
## enables as well (an Elasticsearch left behind after a switch to OpenSearch still holds esdata).
## Volumes are kept - restoreVolume decides per volume what to replace.
function stopEnvironment() {
logVerbose "Environment path: ${ROLL_ENV_PATH}"

Expand All @@ -762,15 +784,15 @@ function stopEnvironment() {
return 0
fi

logVerbose "Checking for running containers with project: ${ROLL_ENV_NAME}"
local running_containers
running_containers="$(docker ps --filter "label=com.docker.compose.project=${ROLL_ENV_NAME}" -q)" || true
logVerbose "Checking for containers with project: ${ROLL_ENV_NAME}"
local env_containers
env_containers="$(docker ps -a --filter "label=com.docker.compose.project=${ROLL_ENV_NAME}" -q)" || true

if [[ -n "${running_containers}" ]]; then
if [[ -n "${env_containers}" ]]; then
logMessage INFO "Stopping environment..."
"${ROLL_DIR}/bin/roll" env down || true
"${ROLL_DIR}/bin/roll" env down --remove-orphans || true
Comment on lines +791 to +793
else
logVerbose "No running containers for this environment"
logVerbose "No containers for this environment"
fi

return 0
Expand Down
Loading