Docker Compose Build Caching#
Overview#
Dokploy's application builds have a per-app cleanCache toggle (merged in PR #1508) that injects --no-cache (Docker/Nixpacks/Paketo/Railpack) or --clear-cache (Heroku) into each builder's command. This field is stored as a boolean column on the application table and defaults to false .
Docker Compose deployments have no equivalent setting. The compose table schema contains no cleanCache column, and the compose builder never passes --no-cache or --pull to Docker.
How Compose Builds Work Today#
createCommand in packages/server/src/utils/builders/compose.ts generates one of two fixed commands per composeType:
composeType | Command issued |
|---|---|
docker-compose | docker compose -p <appName> -f <path> up -d --build --remove-orphans |
stack | docker stack deploy -c <path> <appName> --prune --with-registry-auth |
Neither command includes --no-cache or --pull. As a result, Docker reuses its layer cache on every redeploy β source-code changes that don't invalidate a COPY/ADD layer are silently skipped, and base images are never re-fetched unless the cache is manually busted .
The custom command override field is sanitized by sanitizeCommand, which blocks shell control characters including ; and |. && chaining is allowed only when every chained sub-command starts with docker compose β so a multi-step pattern like docker compose pull && docker compose down && docker compose up -d --build is expressible in principle but must match this exact structure .
The Stale-Build Problem#
Because --remove-orphans is the only cleanup flag on the default docker-compose command, topology changes β renamed services, removed services that owned volumes, changed network names β can leave the stack in an inconsistent state after a redeploy. Old containers or networks that --remove-orphans cannot fully prune remain running alongside the new stack .
The reporter in issue #4992 notes that the expected idiomatic fix (docker compose pull && docker compose down && docker compose up -d --build --remove-orphans) introduces a brief downtime window: containers are fully stopped between down and up, unlike Compose's default in-place rolling recreate. This trade-off should be weighed before adding it as the default.
Note: This problem only applies to
composeType === "docker-compose". Swarm (stack) deployments usedocker stack deploy --prune, which handles image updates via--with-registry-authand does not need a separatepull/downstep .
Feature Gap vs. Application Builds#
| Capability | Application | Compose |
|---|---|---|
cleanCache / --no-cache toggle | β
application.cleanCache | β Not implemented |
--pull (re-fetch base images) | β Not implemented | β Not implemented |
| Multi-builder support | Docker, Nixpacks, Heroku, Paketo, Railpack | Single docker compose up --build path |
| Custom command override | N/A | β
compose.command, sanitized |
Key Files#
| File | Purpose |
|---|---|
packages/server/src/utils/builders/compose.ts | createCommand and getBuildComposeCommand β the entire Compose deploy script |
packages/server/src/db/schema/compose.ts | Compose table schema β no cleanCache field present |
packages/server/src/db/schema/application.ts | Application table schema β cleanCache boolean, default false |
packages/server/src/utils/builders/docker-file.ts | Adds --no-cache when application.cleanCache is true |
packages/server/src/utils/builders/nixpacks.ts | Adds --no-cache when application.cleanCache is true |
Workarounds (Current State)#
-
Custom command override β set
compose.commandto a&&-chained sequence such as:compose -p myapp -f docker-compose.yml pull && compose -p myapp -f docker-compose.yml down && compose -p myapp -f docker-compose.yml up -d --build --remove-orphansThe
compose.commandvalue is prefixed withdockerby the builder, so each chained sub-command must start withdocker composeordocker-composeto pass sanitization . This introduces a downtime window. -
SSH / host-level cache prune β run
docker builder pruneordocker system pruneon the host before triggering a redeploy. This is outside Dokploy's control plane.
Implementing a Fix#
To mirror the application cleanCache feature for Compose, the minimum changes are:
- Add a
cleanCache booleancolumn to thecomposetable inpackages/server/src/db/schema/compose.tsand generate a Drizzle migration. - In
createCommand, append--no-cacheafter--buildwhencompose.cleanCacheis true (applies todocker-composetype only; Swarm does not use--build). - Optionally add a
--pullflag to theupcommand to force base-image refresh. - Expose the toggle in the UI, mirroring the application
cleanCacheswitch added in PR #1508.