Head-Up
This was AI-written because I can't code, but maybe this is true and helpful.
Summary
CleanupDocker runs docker container prune -f --filter "label=coolify.managed=true" after every service deletion. This indiscriminately removes any stopped container managed by Coolify — including standalone databases that are temporarily stopped (crash, OOM, restart loop). This permanently destroys production databases without any warning or soft-delete record.
Steps to Reproduce
- Have a standalone PostgreSQL (or Redis) database running and healthy.
- The database container crashes or stops briefly (OOM, healthcheck failure, temporary blip).
- Delete any service in the same Coolify instance — this triggers
DeleteResourceJob → StopService → DeleteService, each of which dispatches CleanupDocker.
CleanupDocker runs docker container prune -f --filter "label=coolify.managed=true" while the DB container is in Exited state.
- The standalone database container is permanently removed. Its volume may be subsequently pruned by Docker garbage collection or a later cleanup pass.
Observed Behavior
- Standalone
Database Postgres container (postgres-data-<uuid>) and its volume were completely gone from Docker after the above sequence.
- The Coolify DB record for the postgres survived (no
deleted_at, no soft delete) — only the Docker container and volume were destroyed.
DatabaseStatusChanged event fired ~14 seconds after the service deletion, confirming Docker detected the resource as gone.
- No warning, no activity log entry, no UI notification.
- Service depending on the database (trigger.dev) silently failed with no upstream.
Root Cause
In DeleteResourceJob.php (in the finally block) and in both StopService.php and DeleteService.php, CleanupDocker::dispatch($server, false, false) is called — resulting in 3 parallel CleanupDocker dispatches per service deletion.
CleanupDocker.php then runs:
'docker container prune -f --filter "label=coolify.managed=true" --filter "label!=coolify.proxy=true"',
This label filter matches all Coolify-managed containers — including standalone databases — not just ephemeral helper/builder containers. Any standalone database that is transiently stopped (crash window, restart loop, OOM) at the time of a service deletion will be removed.
The unless-stopped Docker restart policy creates a race condition: after a crash, the container briefly passes through Exited state before Docker restarts it. If CleanupDocker fires in that window, the container is gone before it can restart.
Expected Behavior
CleanupDocker should only prune ephemeral containers (helper containers, build containers) — not persistent resources like standalone databases. Standalone databases have their own coolify.type=database label that should be used to exclude them from container pruning.
Suggested Fix
Add an exclusion filter for database containers in the prune command:
// Before
'docker container prune -f --filter "label=coolify.managed=true" --filter "label!=coolify.proxy=true"',
// After
'docker container prune -f --filter "label=coolify.managed=true" --filter "label!=coolify.proxy=true" --filter "label!=coolify.type=database"',
Or alternatively, scope CleanupDocker to only target containers with coolify.type=helper or coolify.type=builder labels.
Environment
- Coolify version: 4.0.0-beta.472
- Docker version: (standard install via Coolify)
- Server: VPS (Hetzner), single-server setup
- Affected resources: Standalone PostgreSQL, Standalone Redis
Impact
Data loss risk. Any temporarily-stopped standalone database can be silently and permanently destroyed the next time a service is deleted from the Coolify UI. The Coolify database record survives (misleading the user into thinking the resource still exists), but the Docker container and volume are gone.
Head-Up
This was AI-written because I can't code, but maybe this is true and helpful.
Summary
CleanupDockerrunsdocker container prune -f --filter "label=coolify.managed=true"after every service deletion. This indiscriminately removes any stopped container managed by Coolify — including standalone databases that are temporarily stopped (crash, OOM, restart loop). This permanently destroys production databases without any warning or soft-delete record.Steps to Reproduce
DeleteResourceJob→StopService→DeleteService, each of which dispatchesCleanupDocker.CleanupDockerrunsdocker container prune -f --filter "label=coolify.managed=true"while the DB container is inExitedstate.Observed Behavior
Database Postgrescontainer (postgres-data-<uuid>) and its volume were completely gone from Docker after the above sequence.deleted_at, no soft delete) — only the Docker container and volume were destroyed.DatabaseStatusChangedevent fired ~14 seconds after the service deletion, confirming Docker detected the resource as gone.Root Cause
In
DeleteResourceJob.php(in thefinallyblock) and in bothStopService.phpandDeleteService.php,CleanupDocker::dispatch($server, false, false)is called — resulting in 3 parallel CleanupDocker dispatches per service deletion.CleanupDocker.phpthen runs:'docker container prune -f --filter "label=coolify.managed=true" --filter "label!=coolify.proxy=true"',This label filter matches all Coolify-managed containers — including standalone databases — not just ephemeral helper/builder containers. Any standalone database that is transiently stopped (crash window, restart loop, OOM) at the time of a service deletion will be removed.
The
unless-stoppedDocker restart policy creates a race condition: after a crash, the container briefly passes throughExitedstate before Docker restarts it. IfCleanupDockerfires in that window, the container is gone before it can restart.Expected Behavior
CleanupDockershould only prune ephemeral containers (helper containers, build containers) — not persistent resources like standalone databases. Standalone databases have their owncoolify.type=databaselabel that should be used to exclude them from container pruning.Suggested Fix
Add an exclusion filter for database containers in the prune command:
Or alternatively, scope
CleanupDockerto only target containers withcoolify.type=helperorcoolify.type=builderlabels.Environment
Impact
Data loss risk. Any temporarily-stopped standalone database can be silently and permanently destroyed the next time a service is deleted from the Coolify UI. The Coolify database record survives (misleading the user into thinking the resource still exists), but the Docker container and volume are gone.