2026.09 / deployment queue 053
Coolify deployment stuck queued or in progress? Diagnose it safely
A Coolify deployment that stays queued has not necessarily failed, and one that stays in_progress is not necessarily still building. The useful question is more precise: which deployment is waiting, what resource or operation owns the wait, and is production still serving the previous healthy release?
This runbook is for a self-hosted Coolify application whose deployment does not reach a terminal state. It separates queue capacity from a silent build, a browser log-view problem, exhausted disk or memory, and a control-plane fault. It also avoids the two tempting destructive shortcuts: repeatedly adding deployments to the queue and running an unscoped Docker prune.
Preserve the deployment ID and its last useful log line before cancelling or restarting anything. A queue badge without that evidence is not a diagnosis.
First classify the symptom
| What you observe | Most useful next check |
|---|---|
queued, with another build active | Server build concurrency and the active deployment’s progress |
queued, with no build apparently active | Exact deployment record, Coolify worker/control-plane logs, and server health |
in_progress, logs still advancing | The current command, elapsed time, CPU, memory, network and disk activity |
in_progress, no new log output | Whether the build process still exists and whether the dashboard view itself is stale |
| Deployment says complete but the site is old | The deployed commit, container, router and caches—not the queue |
Refresh the deployment page once and reopen the exact record. A log stream can stop updating when the browser sleeps or its realtime connection drops while the server-side operation continues. Compare the UI with the deployment API or the server’s actual Docker activity before treating a frozen log panel as a frozen build.
Identify one deployment, not just the application
Record the application UUID, source branch and commit, deployment ID, creation time, current status, and final visible log line. Coolify’s deploy endpoint returns deployment details when a request is queued. Treat the returned deployment identifier as an opaque string; do not assume it has a particular UUID shape.
# Generic API shapes; keep the real bearer token private
curl --fail --silent --show-error \
-H 'Authorization: Bearer REDACTED' \
https://coolify.example.com/api/v1/deployments/DEPLOYMENT_ID
curl --fail --silent --show-error \
-H 'Authorization: Bearer REDACTED' \
https://coolify.example.com/api/v1/applications/APP_UUID
If your installed version exposes different routes, use that version’s API documentation or run Coolify’s local route listing privately. Do not paste full API responses into a public issue without reviewing them: application records and logs can disclose repository URLs, domains, environment names, or credentials.
If it is queued, inspect build capacity before touching workers
Coolify limits concurrent builds per server. A queued deployment behind a real active build can be normal back-pressure. In the server settings, check the configured concurrent-build limit. Then inspect all current deployments: a slow image build, package download, or health-gated rollout at the front of the line can explain every later queue entry.
- Stop clicking Deploy; duplicate requests make the queue harder to read.
- Find the oldest active deployment and open its output.
- Decide whether its command is making progress or waiting indefinitely.
- Increase concurrency only if the server has measured CPU, memory and disk headroom.
More parallel builds are not free throughput. On a small VPS they can create memory pressure, slower disk I/O, package-manager timeouts, or an out-of-memory kill. Coolify’s own server-crash guidance recommends using a separate build server, moving image builds to CI, or increasing resources when builds overload the deployment host.
If it is in progress, follow the last command
The final visible line usually identifies the boundary. Do not start by restarting the application container; builds run outside the old serving container, and the previous release may still be handling production traffic correctly.
| Last operation | Evidence to collect |
|---|---|
| Cloning or fetching Git | Repository authentication, DNS and outbound HTTPS/SSH from the build host |
| Pulling a base image | Registry status, rate limits, credentials, DNS and free space |
| Installing packages | Whether output advances, lockfiles, registry reachability and memory use |
| Building an image | Docker build process, BuildKit cache, host CPU/memory, disk bytes and inodes |
| Starting the replacement | Application logs, port binding, migrations and dependency readiness |
| Waiting for health | The effective probe, executable, internal port, path, status and start period |
On the server, begin with read-only checks:
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
docker system df
df -h
df -i
free -h
docker logs --since 20m coolify
# Use the actual worker/container name shown by docker ps:
docker logs --since 20m COOLIFY_WORKER_CONTAINER
Inspect only the relevant log window and redact before sharing. docker system df reports Docker’s disk use; df -h catches full filesystems, while df -i catches inode exhaustion that a gigabyte summary can miss. Kernel OOM evidence and a vanished build process point toward resource exhaustion rather than a bad application command.
Clean disk through the narrowest safe path
If storage is genuinely the blocker, prefer Coolify’s automated Docker cleanup configuration. Its documentation says cleanup can remove stopped containers, unused images and build cache according to a schedule or disk threshold. Review what your installed version selects before running it.
Do not jump to docker system prune --all --volumes. Docker documents that system prune removes unused containers, networks, images and build cache, with volumes included only when explicitly requested. On a multi-application server, broad cleanup can remove rollback images, caches needed by another build, or unused-looking volumes that still matter operationally. Check reclaimable categories, backups and rollback needs first; never prune volumes as queue troubleshooting.
Cancel only after preserving evidence
Coolify provides an authenticated endpoint to cancel a deployment by its deployment UUID. Use the specific stalled deployment, not a mass kill, and expect API availability or response details to vary with the installed Coolify version.
curl --fail --silent --show-error -X POST \
-H 'Authorization: Bearer REDACTED' \
https://coolify.example.com/api/v1/deployments/DEPLOYMENT_ID/cancel
Cancel when the underlying operation is conclusively stuck, not merely slow. After cancellation, confirm the deployment reaches a terminal cancelled state and check whether the build process/container was removed. If it remains, investigate the Coolify worker and installed-version issues before adding a new attempt.
Restart in layers, not all at once
- If only the browser log stream is stale, reopen it; do not restart the server.
- If one build is stuck, cancel that deployment and confirm cleanup.
- If all new work queues while the dashboard and Docker are healthy, inspect Coolify’s worker/control-plane logs and health.
- Restart only the affected Coolify service when its fault is evidenced and you know the installation’s supported procedure.
- Reboot the host only for a host-level fault that narrower recovery cannot resolve.
Before control-plane maintenance, verify whether any database backup, restore, migration or deployment is active. Keep a current Coolify backup and know the documented recovery route. A blind restart can erase the most useful transient evidence and interrupt unrelated applications.
Retry once, then verify the replacement
Repair the diagnosed cause before retrying: free measured space, correct Git access, fix the Dockerfile command, add build capacity, or restore the worker. Trigger one deployment and follow its returned ID to a terminal state. A queued response is not success.
curl --fail --silent --show-error -X POST \
-H 'Authorization: Bearer REDACTED' \
-H 'Content-Type: application/json' \
--data '{"uuid":"APP_UUID","force_rebuild":false}' \
https://coolify.example.com/api/v1/deploy
After the deployment reports success, prove the intended release rather than accepting HTTP 200:
- Match the successful deployment to the intended Git commit.
- Confirm the replacement container is running and, when configured, healthy.
- Fetch a unique heading, version endpoint, or changed response from the public hostname.
- Check an intentionally nonexistent path does not return the homepage through a fallback.
- Verify critical API, asset and persistence paths—not only the root page.
- Confirm the old release remains available for rollback until acceptance passes.
curl --fail --silent --show-error \
'https://app.example.com/release-check?verify=EXPECTED_COMMIT' | \
grep -F 'EXPECTED RELEASE MARKER'
curl --silent --output /dev/null --write-out '%{http_code}\n' \
https://app.example.com/this-path-must-not-exist
A compact decision checklist
- The exact deployment ID, source commit, status and last log line are recorded.
- A stale browser/realtime view has been ruled out.
- Queued work has been compared with active builds and the concurrency limit.
- The final operation has been mapped to Git, registry, build, startup or health.
- Host memory, Docker disk usage, filesystem bytes and inodes have been checked.
- No broad prune, volume deletion or host reboot was used as a first step.
- Only the proven-stalled deployment was cancelled.
- One retry reached a terminal state and matches the intended commit.
- Unique live content and important routes pass after deployment.
Authoritative references: Coolify’s official cancel-deployment API reference, FAQ entry on concurrent builds, server crash during build guide, and automated Docker cleanup guide. Docker’s official references explain docker system df and exactly what docker system prune removes.