fix(stacks): resolve permission denied error on stack deletion (#261)

* fix(stacks): resolve permission denied error when deleting stacks with root-owned files

When Docker Compose creates files as root inside a stack directory, the
non-root Sencho process cannot remove them. This adds a Docker-based
fallback: if fsPromises.rm fails with EACCES/EPERM, Sencho spawns a
short-lived Alpine container to clean up the root-owned files.

Also enhances docker compose down with --volumes --remove-orphans to let
Docker clean up its own resources before filesystem deletion.

* docs: clarify that pre-existing root-owned stacks can be deleted

* fix(stacks): include Docker stderr in fallback deletion error message

Fixes CI lint failure: 'stderr' was assigned but never read in
forceDeleteViaDocker(). Now surfaces Docker stderr output in the error
message when the fallback cleanup fails.
This commit is contained in:
Anso
2026-03-29 21:32:05 -04:00
committed by GitHub
parent f760ea6563
commit 116f15dae9
6 changed files with 272 additions and 6 deletions
+5 -1
View File
@@ -49,12 +49,16 @@ The stack header exposes four actions:
| **Stop** | `docker compose stop` | Stops containers without removing them. State is preserved. |
| **Restart** | `docker compose restart` | Restarts all containers in the stack. |
| **Update** | `docker compose pull` + `up -d` | Pulls the latest image tags and recreates containers. |
| **Delete** | `down` + removes files | Stops and removes containers, then deletes the stack directory. |
| **Delete** | `down --volumes` + removes files | Stops and removes containers and volumes, then deletes the stack directory. |
<Warning>
**Delete** is irreversible. It removes the stack directory - including `compose.yaml`, `.env`, and any bind-mounted files stored there. Back up important files before deleting.
</Warning>
<Note>
Stack directories often contain root-owned files — either because Docker Compose creates them, or because the stacks were originally set up outside of Sencho with `sudo`. Sencho handles this automatically: if a normal deletion fails due to permissions, it uses Docker to clean up root-owned files. No manual intervention is needed in the standard setup.
</Note>
## Stack context menu
Right-click or use the **⋮** button on any stack in the sidebar to access:
+20
View File
@@ -65,6 +65,26 @@ If the group is not `docker`, the auto-detection still works - Sencho reads the
---
## "Permission denied" when deleting a stack
**Symptom:** Clicking Delete on a stack fails with a permission error mentioning `EACCES`.
**Cause:** Stack directories often contain files owned by root — for example when Docker Compose was run with `sudo`, or when containers write config/data files into the stack directory. Since Sencho runs as a non-root user, the OS denies the removal.
This is especially common when installing Sencho on a server where stacks were originally created outside of Sencho (e.g. via `sudo docker compose up`). Those directories and their contents are root-owned, but Sencho can still delete them.
**How Sencho handles it:** Sencho automatically detects permission errors during deletion and falls back to a Docker-based cleanup. It spawns a short-lived Alpine container that bind-mounts the stack directory and removes the root-owned files. This happens transparently — no manual intervention is needed in the standard Docker setup.
**If automatic cleanup fails:** The error message will include the directory path. Remove it manually:
```bash
sudo rm -rf /path/to/your/compose/dir/stack-name
```
**Prerequisites:** The Docker socket must be mounted (standard setup). If Sencho cannot access Docker, the fallback will not work — see [Permission denied on the Docker socket](#permission-denied-on-the-docker-socket).
---
## Login page shows "Something went wrong"
**Symptom:** You enter credentials and get a generic error instead of being logged in.