IAnswerable

Deploy Any Docker Compose App with Dokploy

24 Oct 2025

If an app already has a docker-compose.yml, you do not need to rewrite anything to run it on Dokploy. There is a compose service type for exactly this.

Create a Compose service

In your project, Create Service → Compose. Point it at a git repository, or paste the compose file directly.

Dokploy runs it under Docker Swarm, so a few compose features behave differently. More on that below.

Expose a service to the internet

Compose files usually publish ports like this:

services:
  app:
    image: myorg/app:latest
    ports:
      - "3000:3000"

Do not do that here. Publishing a host port bypasses Traefik and you lose automatic SSL. Instead, remove the ports block and add a domain in the Dokploy UI under the compose service’s Domains tab:

Traefik routes to the container over the internal network. No host port needed.

Volumes

Named volumes work as expected:

volumes:
  pgdata:

services:
  db:
    image: postgres:16
    volumes:
      - pgdata:/var/lib/postgresql/data

Bind mounts to relative paths do not. ./config:/app/config resolves relative to where the stack is deployed, which is not where you think. Use an absolute path under /etc/dokploy/ or bake the file into the image.

Environment variables

Reference Dokploy environment variables in the compose file with standard interpolation:

services:
  app:
    image: myorg/app:latest
    environment:
      DATABASE_URL: ${DATABASE_URL}
      NODE_ENV: production

Then set DATABASE_URL in the service’s Environment tab. Keep secrets out of the committed compose file.

What Swarm changes

A few compose keys are ignored or behave differently under Swarm:

The depends_on one bites hardest. Add a retry loop or a wait script; do not assume the database is up.

Healthchecks matter more here

Swarm uses healthchecks to decide whether a container is ready to receive traffic. Without one, Traefik may route requests to a container that is still booting:

    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 20s

start_period is the one people forget. Without it, a slow-booting app gets marked unhealthy and restarted forever.

Deploy and watch

Hit Deploy and open the Logs tab. If a container is looping, the logs will say why within a few seconds. The most common causes are a missing environment variable and a volume permission error.