Dokploy / Dokploy/dokploy

Concurrent domain creation causes race condition - only one domain gets added to Traefik config

Open
#3,029 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
37.4k
Forks
3k
Avg merge
1d 3h
Merged PRs (30d)
73

Description

Summary

When creating multiple domains for the same application concurrently using the Dokploy API (via Promise.all with multiple domainCreate calls), only one of the domains gets added to the Traefik configuration. This appears to be a race condition in how Dokploy updates the Traefik config when processing concurrent domain creation requests.

Environment

  • Dokploy API endpoint: https://app.dokploy.com/api
  • API authentication: Using API key via x-api-key header
  • Application type: Docker application

Steps to Reproduce

  1. Create a Dokploy application using the API
  2. Attempt to create multiple domains for the same application concurrently:
await Promise.all([
  domainCreate({
    baseUrl: "https://app.dokploy.com/api",
    headers: { "x-api-key": process.env.DOKPLOY_API_KEY },
    body: {
      applicationId,
      host: \`one-\${id}.example.com\`,
      port: 3000,
      https: true,
      domainType: "application",
      certificateType: "letsencrypt",
    },
    throwOnError: true,
  }),
  domainCreate({
    baseUrl: "https://app.dokploy.com/api",
    headers: { "x-api-key": process.env.DOKPLOY_API_KEY },
    body: {
      applicationId,
      host: \`two-\${id}.example.com\`,
      port: 3001,
      https: true,
      domainType: "application",
      certificateType: "letsencrypt",
    },
    throwOnError: true,
  }),
]);

Expected Behavior

Both domains should be created and added to the Traefik configuration, with both domains being accessible and properly routed.

Actual Behavior

Only one of the domains (seemingly random which one) gets added to the Traefik configuration. The other domain appears to be created in the database but is not reflected in the Traefik config.

Workaround

Calling the `domainCreate` API endpoints sequentially instead of concurrently resolves the issue:

await domainCreate({
  baseUrl: "https://app.dokploy.com/api",
  headers: { "x-api-key": process.env.DOKPLOY_API_KEY },
  body: {
    applicationId,
    host: \`one-\${id}.example.com\`,
    port: 3000,
    https: true,
    domainType: "application",
    certificateType: "letsencrypt",
  },
  throwOnError: true,
});

await domainCreate({
  baseUrl: "https://app.dokploy.com/api",
  headers: { "x-api-key": process.env.DOKPLOY_API_KEY },
  body: {
    applicationId,
    host: \`two-\${id}.example.com\`,
    port: 3001,
    https: true,
    domainType: "application",
    certificateType: "letsencrypt",
  },
  throwOnError: true,
});

Root Cause Analysis

This appears to be a race condition where concurrent domain creation requests for the same application conflict when updating the Traefik configuration file. Possible causes:

  • Lack of locking mechanism when updating Traefik config
  • Missing transaction isolation when processing concurrent domain creation requests
  • Race condition in the Traefik config regeneration process

Impact

  • API consumers must implement sequential domain creation, increasing latency
  • Potential data inconsistency between database state and Traefik configuration
  • Unexpected behavior when using the API programmatically

Suggested Fix

Implement proper synchronization/locking when updating the Traefik configuration, or ensure that domain creation operations for the same application are queued and processed sequentially at the API level.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the domainCreate API entry point and trace how a domain is persisted and how the Traefik configuration is regenerated. Reproduce the issue with the Promise.all example, then verify that both domains remain in the database and appear in the Traefik configuration.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, typescript
Domain
backend, devops
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.