Skip to content

Compose builder truncates a repo-tracked .env, even with an empty Environment tab #4956

Description

@proph37

Compose builder truncates a repo-tracked .env, even with an empty Environment tab

Description

For Compose applications deployed from a Git source, Dokploy writes its own .env into the compose file's directory on every deploy, discarding a .env that is committed in the repository. This happens even when the Environment tab is empty.

This is the same class of problem as #2835, which was fixed for the Docker/Dockerfile builder by #3180 ("refactor(docker): remove unused environment file command generation"). The Compose builder was not covered and still behaves the old way on main.

The impact is larger for Compose than it was for the Dockerfile builder, because Docker Compose automatically loads any file named .env from the compose file's directory for variable interpolation. A repository that commits its own .env therefore cannot deploy through Dokploy: every ${VAR} in compose.yml resolves to empty, and env_file: [.env] reads Dokploy's replacement rather than the committed file.

Version

Dokploy v0.29.13. Code below is from main at a4961503, where it is unchanged.

Steps to reproduce

  1. Create a Git repository containing myservice/compose.yml and a committed myservice/.env.
  2. In Dokploy, create a Compose application from that repository, with Compose Path ./myservice/compose.yml.
  3. Leave the Environment tab completely empty.
  4. Deploy.
  5. Inspect the file on the server:
md5sum /etc/dokploy/compose/<app-name>/code/myservice/.env
cat    /etc/dokploy/compose/<app-name>/code/myservice/.env

Expected behaviour

Per #2835, "the .env file is kept as it is when it exists". Dokploy's own variables can be supplied through the process environment, as that issue notes.

Actual behaviour

The committed file is replaced. Observed on a real deployment:

before deploy   f894431d2b47e773e4cd72ed9e8591c4   (committed file, 6667 bytes)
during deploy   (file absent - repo is re-cloned)
after deploy    dc4e69d0613c1a0004a73a674af38787   (127 bytes)

The resulting file contains only:

APP_NAME=<app-name>
COMPOSE_PROJECT_NAME=<app-name>
DOCKER_CONFIG=/root/.docker

Cause

getCreateEnvFileCommand in packages/server/src/utils/builders/compose.ts:

const envFilePath = join(dirname(composeFilePath), ".env");

let envContent = `APP_NAME=${appName}\n`;
envContent += `COMPOSE_PROJECT_NAME=${appName}\n`;
envContent += env || "";
if (!envContent.includes("DOCKER_CONFIG")) {
    envContent += "\nDOCKER_CONFIG=/root/.docker";
}
...
echo "${encodedContent}" | base64 -d > ${quote([envFilePath])};

envContent is built from scratch and written with a truncating >, so any existing file at envFilePath is discarded. The existing file is never read.

Two smaller observations in the same function:

  • if (!envContent.includes("DOCKER_CONFIG")) tests the string just constructed rather than the file on disk, so it is always true unless the user's own Environment tab defines it.
  • join(COMPOSE_PATH, appName, "code", composePath) || join(..., "docker-compose.yml") - the left operand is always a non-empty string, so the || fallback is unreachable.

Possible fix

Mirror what #3180 did for the Docker builder: when a .env already exists in the compose directory, leave it alone and pass APP_NAME / COMPOSE_PROJECT_NAME / DOCKER_CONFIG plus any Environment-tab values through the process environment of the docker compose invocation instead. docker compose reads both, with the process environment taking precedence over the .env file, so interpolation keeps working and the committed file survives.

If overwriting must stay the default, merging into the existing file rather than truncating it would at least preserve committed values.

Workaround

Name the file something Dokploy does not generate (e.g. service.env) and reference it explicitly:

services:
  app:
    env_file:
      - service.env

Note this only covers variables consumed inside containers. Anything the Compose parser itself needs - ${VAR} in volumes:, networks:, or image tags - cannot come from env_file: and has to be made a literal or moved into the Environment tab.

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