Portainer: Deleting a Broken Stack Throwing Errors on Linux

Status: CURRENT — RECOVERY WORKAROUND
Last reviewed: 31 August 2026
Applies to: Portainer CE/BE on Docker Standalone when a stack exists in Portainer but its stored Compose file is missing

This is still a real Portainer failure mode. Recent Portainer issues show stacks can become undeletable when the stored /data/compose/<stack-id> directory or Compose file disappears. This workaround recreates the minimum Portainer expects so the stack can be deleted normally.

Every now and again Portainer manages to get itself into a stupid state where a stack still exists in its database, but the Compose file behind it has disappeared.

You then try to delete the broken stack and Portainer throws something along the lines of:

could not get the contents of the file 'docker-compose.yml'

or

open /data/compose/234/docker-compose.yml:
no such file or directory


Excellent. The stack is broken because the file is missing, and Portainer refuses to delete the broken stack because the file is missing. Very helpful 😂.

Is this still relevant?

Yep.

This bug has been reported in Portainer for years, and there are still current examples. A July 2026 Portainer issue describes a failed Git Pull and redeploy operation leaving the stack’s /data/compose/<id> directory without the Compose file, after which the stack cannot be edited or redeployed normally.

Older reports describe the same basic workaround as this article: recreate the missing directory/file, then let Portainer perform the deletion itself.

Important: we’re touching Portainer’s persistent /data directory directly. Don’t start randomly deleting folders in there because some bloke on the internet — including me — said so. Back up Portainer first 🙂

1. Find the stack ID

The easiest clue is normally the error itself. Portainer may show a path such as:

/data/compose/234/docker-compose.yml


In that example the stack ID is:

234


You may also be able to see the stack ID in the Portainer URL when viewing the stack.

2. Find where Portainer’s /data actually lives

My old article assumed Portainer used the default named volume at:

/var/lib/docker/volumes/portainer_data/_data


That’s common, but it is not guaranteed. Portainer might be using a different named volume or a bind mount.

Ask Docker where the Portainer container’s /data mount actually points:

docker inspect portainer   --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Source}}{{end}}{{end}}'


For a standard named volume that may return:

/var/lib/docker/volumes/portainer_data/_data


If your Portainer container isn’t actually called portainer, find it first:

docker ps --format 'table {{.Names}}\t{{.Image}}' | grep -i portainer


3. Back up Portainer before poking it

Set the returned path as a variable:

PORTAINER_DATA="$(docker inspect portainer   --format '{{range .Mounts}}{{if eq .Destination "/data"}}{{.Source}}{{end}}{{end}}')"

echo "$PORTAINER_DATA" 


Make sure that printed the correct Portainer data directory before continuing.

Then take a quick filesystem backup:

sudo tar -czf "/root/portainer-data-backup-$(date +%Y%m%d-%H%M%S).tar.gz"   -C "$PORTAINER_DATA" .


For a tiny recovery job this takes bugger-all effort and gives you an escape hatch if your fingers become enthusiastic.

4. Check the Compose storage

List Portainer’s stored stack directories:

sudo ls -lah "$PORTAINER_DATA/compose/" 


If your broken stack is ID 234, check it directly:

sudo ls -lah "$PORTAINER_DATA/compose/234/" 


You will normally find one of two things:

  • The entire 234 directory is missing.
  • The directory exists but docker-compose.yml is missing.

5. Recreate only what Portainer is complaining about

If the whole stack directory is missing:

sudo mkdir -p "$PORTAINER_DATA/compose/234" 


Now create the missing Compose file:

sudo nano "$PORTAINER_DATA/compose/234/docker-compose.yml" 


It does not need to recreate your entire original stack if your only goal is to get rid of the dead Portainer record.

I put a single YAML comment in it:

# recovery placeholder so Portainer can delete the broken stack


Save the file.

You can do the same thing without opening an editor:

echo '# recovery placeholder so Portainer can delete the broken stack' |   sudo tee "$PORTAINER_DATA/compose/234/docker-compose.yml" >/dev/null


6. Try deleting the stack again

Go back to:

Portainer → Stacks → broken stack → Delete this stack


If the missing Compose file was the blocker, Portainer should now be able to finish deleting the stale stack record.

That’s it. We essentially gave Portainer the world’s saddest Compose file so it would finally agree to clean up its own mess 😀

What if it complains about stack.env instead?

Another reported version of this bug complains about:

failed to create env file:
open /data/compose/234/stack.env:
no such file or directory


In that case, recreating the missing stack directory may be enough because Portainer can then create stack.env itself.

If the error specifically says a required file is missing, follow the error rather than blindly manufacturing a dozen random files.

Git-backed stacks are a bit different

If the stack originally came from Git, first consider whether restoring the Compose file/repository path is the cleaner fix.

Portainer’s current Git stack handling still expects the configured Compose path to exist. Renaming/removing that file can leave Portainer unable to redeploy or delete the stack cleanly.

If you only want the stale Portainer entry gone and the Git source is already toast, the placeholder-file recovery above is still useful.

What I would NOT do

  • Don’t delete the entire portainer_data volume. That’s the nuclear option and wipes Portainer’s persistent state.
  • Don’t start editing Portainer’s database manually just to remove one broken stack unless you absolutely know what you’re doing.
  • Don’t delete random numbered directories from /data/compose.
  • Don’t assume the stack ID is the same thing as a Docker container ID.

The goal here is deliberately boring: identify the exact missing stack directory, recreate the minimum expected file, let Portainer delete it normally, fuck off and do something more interesting.

Old command from the original article

The original guide started with:

sudo ls /var/lib/docker/volumes/portainer_data/_data/compose/


That is still correct if Portainer uses a Docker named volume called portainer_data. The current article simply discovers the mount first so the same fix also works with custom volumes and bind mounts.

References

A very small fix for a very annoying circular error. Portainer: “I can’t delete the stack because the file doesn’t exist.” Me: “Fine, here’s a fucking comment file.” Portainer: “Oh okay then.” 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *