Skip to content

Raw stack compose drops tmpfs and additional long-syntax mounts on remote server #4794

Closed

Description

@yusoofsh

Environment

  • Dokploy: v0.29.11
  • Compose source: raw
  • Compose type: stack
  • Target: remote Docker Swarm server

Reproduction

Save a raw stack compose containing either supported tmpfs form:

services:
  test:
    image: alpine:latest
    command: ["sleep", "infinity"]
    volumes:
      - type: tmpfs
        target: /scratch

or:

services:
  test:
    image: alpine:latest
    command: ["sleep", "infinity"]
    tmpfs:
      - /scratch

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.

Activity

  1. yusoofsh commented on Jul 11, 2026

    @yusoofsh
    ContributorAuthor

    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:

    Initializing deployment
    File 'docker-compose.yml' created: ✅
    Detected: 0 mounts
    Command: docker stack deploy -c docker-compose.yml <stack> --prune --with-registry-auth
    Source Type: docker raw ✅
    Compose Type: stack ✅
    ❌ Error: Compose file not found
    

    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.

  2. dosubot commented on Jul 11, 2026

    @dosubot

    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 contains exit 1 before docker stack deploy ever runs.

    The sequence is:

    1. Session 1 (lines 254–255): Execute getCreateComposeFileCommand on remote — writes the raw compose file [5]
    2. 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]
    3. 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.

    To reply, just mention @dosu.


    Share context across your team and agents. Try Dosu.

    Leave Feedback Learn about dokploy with Dosu Add Dosu to your team

  3. added a commit that references this issue on Aug 12, 2026
    e43d99e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions