You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Raw stack compose drops tmpfs and additional long-syntax mounts on remote server #4794
Then update the compose, inspect Show Converted Compose, and deploy it.
Observed
The raw source retains the tmpfs declaration.
The converted compose does not contain /scratch.
The deployed Swarm service has no tmpfs mount.
Additional long-syntax named-volume mounts added in the same way were also absent from the converted/runtime specification.
Rewriting named-volume mounts using short syntax (name:/path) was preserved and deployed correctly.
Expected
Both tmpfs forms and long-syntax mounts should survive raw-source conversion and reach docker stack deploy unchanged (apart from Dokploy's intentional service/network/domain transformations).
Notes
The current canary source types already include tmpfs, and addSuffixToVolumesInServices appears to return non-type: volume mount objects unchanged. This may therefore be in the raw remote compose materialization/update path or stale converted-file handling rather than the volume suffix helper itself.
A minimal regression test covering raw + stack + remote-server conversion would help identify the exact layer before patching.
The compose record retains the corrected raw source, but composeStatus becomes error, Show Converted Compose has no service mount from the pending source, and the existing 0/0 Swarm service retains its previous specification.
This supports the suspected remote raw-compose materialization/stale-file path rather than addSuffixToVolumesInServices. It also reproduces without domains or running replicas, which may help narrow the regression test.
Hi @yusoofsh! I'm Dosu and I'm helping the Dokploy team.
This additional evidence strongly confirms the root cause is in the deployment sequencing. I traced the full flow and found the critical issue:
The "❌ Error: Compose file not found" message is generated at build time on the Dokploy server, not at runtime on the remote. Here's why this matters:
In getBuildComposeCommand[1], writeDomainsToCompose is called as an await — meaning it executes immediately on the Dokploy server while assembling the bash script. Inside it, loadDockerComposeRemote[2] runs cat ${path} on the remote server via SSH to read the file back. If it fails (file not found or stderr is non-empty), it returns null, and writeDomainsToCompose returns the error bash snippet [3]:
if(!composeConverted){return`echo "❌ Error: Compose file not found"; exit 1;`;}
That error string is then embedded into the deployment bash script at line 50 [4], which is later sent to the remote for execution. So the deployment command itself containsexit 1 before docker stack deploy ever runs.
The sequence is:
Session 1 (lines 254–255): Execute getCreateComposeFileCommand on remote — writes the raw compose file [5]
Back on Dokploy server (line 275): getBuildComposeCommand calls writeDomainsToCompose, which calls loadDockerComposeRemote — tries to cat the file from the remote via a new SSH session [2]
Session 2 (lines 278): Execute the assembled deployment script on remote [6]
The problem is between steps 1 and 2: loadDockerComposeRemote is failing to read the file that was just written. Since the paths are identical (${COMPOSE_PATH}/${appName}/code/docker-compose.yml) [7][8], the likely explanations are:
SSH connection reuse/caching issue — the second SSH session may be hitting a stale connection or a different node
The first command failed silently — stderr is redirected to the log file (>> ${deployment.logPath} 2>&1), so if the write actually failed, execAsyncRemote might not throw [9]
Filesystem visibility delay on the remote (unlikely for a single host, but possible with networked storage)
Since you're seeing "File 'docker-compose.yml' created: ✅" in logs, the first session appears to succeed. But that message is just an echo in the bash script — it doesn't verify the file actually exists [10]. The script does rm -rf → mkdir -p → base64 -d > file → echo "created ✅" with set -e, so if the write failed, the echo shouldn't run... unless the base64 -d exits 0 despite not writing properly.
A workaround for raw source types would be to skip the remote filesystem round-trip entirely and parse compose.composeFile directly from the DB in writeDomainsToCompose — since the raw content is already available in memory. This is essentially what local deployments do (via loadDockerCompose reading the local file), and would eliminate the dependency on the SSH read-back step entirely.
Activity
yusoofsh commentedon Jul 11, 2026
Additional evidence from the same v0.29.11 remote-Swarm environment:
After updating five raw stack compose objects (all intentionally
replicas: 0), every deployment failed with the same sequence:The compose record retains the corrected raw source, but
composeStatusbecomeserror, Show Converted Compose has no service mount from the pending source, and the existing 0/0 Swarm service retains its previous specification.This supports the suspected remote raw-compose materialization/stale-file path rather than
addSuffixToVolumesInServices. It also reproduces without domains or running replicas, which may help narrow the regression test.dosubot commentedon Jul 11, 2026
Hi @yusoofsh! I'm Dosu and I'm helping the Dokploy team.
This additional evidence strongly confirms the root cause is in the deployment sequencing. I traced the full flow and found the critical issue:
The
"❌ Error: Compose file not found"message is generated at build time on the Dokploy server, not at runtime on the remote. Here's why this matters:In
getBuildComposeCommand[1],writeDomainsToComposeis called as anawait— meaning it executes immediately on the Dokploy server while assembling the bash script. Inside it,loadDockerComposeRemote[2] runscat ${path}on the remote server via SSH to read the file back. If it fails (file not found orstderris non-empty), it returnsnull, andwriteDomainsToComposereturns the error bash snippet [3]:That error string is then embedded into the deployment bash script at line 50 [4], which is later sent to the remote for execution. So the deployment command itself contains
exit 1beforedocker stack deployever runs.The sequence is:
getCreateComposeFileCommandon remote — writes the raw compose file [5]getBuildComposeCommandcallswriteDomainsToCompose, which callsloadDockerComposeRemote— tries tocatthe file from the remote via a new SSH session [2]The problem is between steps 1 and 2:
loadDockerComposeRemoteis failing to read the file that was just written. Since the paths are identical (${COMPOSE_PATH}/${appName}/code/docker-compose.yml) [7] [8], the likely explanations are:>> ${deployment.logPath} 2>&1), so if the write actually failed,execAsyncRemotemight not throw [9]Since you're seeing "File 'docker-compose.yml' created: ✅" in logs, the first session appears to succeed. But that message is just an
echoin the bash script — it doesn't verify the file actually exists [10]. The script doesrm -rf → mkdir -p → base64 -d > file → echo "created ✅"withset -e, so if the write failed, the echo shouldn't run... unless thebase64 -dexits 0 despite not writing properly.A workaround for raw source types would be to skip the remote filesystem round-trip entirely and parse
compose.composeFiledirectly from the DB inwriteDomainsToCompose— since the raw content is already available in memory. This is essentially what local deployments do (vialoadDockerComposereading the local file), and would eliminate the dependency on the SSH read-back step entirely.To reply, just mention @dosu.
Share context across your team and agents. Try Dosu.
fix(compose): Preserve raw remote mounts
chore(release): 0.42.0 [skip ci]