---
title: "How To Deploy A Docker Compose App in Dokploy"
description: "Deploy any Docker Compose app in Dokploy with this step-by-step guide. Covers the Domains tab, Traefik labels, environment variables, and troubleshooting."
date: 2026-08-05
categories: ["self-hosting"]
tags: ["dokploy","docker-compose","self-hosted"]
---

import Button from "../../components/widgets/Button.astro";
import YouTubeEmbed from "../../components/widgets/YouTubeEmbed.astro";
import Notice from "../../components/widgets/Notice.astro";
import ListCheck from "../../components/widgets/ListCheck.astro";
import Accordion from "../../components/widgets/Accordion.astro";
import Tabs from "../../components/widgets/Tabs.astro";
import Tab from "../../components/widgets/Tab.astro";
import { Picture } from "astro:assets";
import img1 from "../../assets/images/24/08/dns-a-rec.png";
import img2 from "../../assets/images/24/08/dokploy-compose.png";
import img3 from "../../assets/images/24/08/dokploy-env.png";
import img4 from "../../assets/images/24/08/dokploy-domain-add.png";

[Dokploy](https://dokploy.com/) is an open-source, self-hostable Platform as a Service (PaaS) for deploying and managing applications with Docker and Traefik. It has 36,300+ GitHub stars and regular releases (currently at v0.29.x), and it's a free alternative to Vercel, Heroku, and Netlify. For comparisons, see [how Dokploy stacks up against Coolify and Kamal 2](/coolify-vs-dokploy-vs-kamal-2/) or our [self-hosted server panels roundup](/best-self-hosted-panels/).

This guide walks through deploying any Docker Compose app in Dokploy. I'll use a Flowise AI + PostgreSQL stack as the running example, but the same workflow applies to any compose file.

<YouTubeEmbed
  url="https://www.youtube.com/embed/mJY4lXbXsPM"
  label="How To Deploy A Docker Compose App in Dokploy"
/>

<Notice type="info" title="Video note">
The video was recorded with an earlier Dokploy version. The workflow is the same, but the UI may look slightly different. The Domains tab is now the recommended method for configuring domains.
</Notice>

## Prerequisites

Before you start, make sure you have everything in place:

<ListCheck>
- A VPS running Linux (Ubuntu 22.04+ or Debian 12+). A Hetzner CX22 at ~€4.49/mo works well. You can get an [affordable VPS from Hetzner](https://go.bitdoze.com/hetzner) or a [budget VPS from Hostinger](https://go.bitdoze.com/hostinger-vps)
- Dokploy installed and accessible at its dashboard URL. See our guide to [install and configure Dokploy on your VPS](/dokploy-install/)
- Docker and Docker Compose v2 installed (the Dokploy installer handles this)
- Ports 80 and 443 open on the VPS firewall (required for Traefik and Let's Encrypt)
- A domain name with DNS access (to create an A record)
- The Docker Compose file for the app you want to deploy
</ListCheck>

<Notice type="warning" title="Ports 80 and 443">
If these ports are blocked by your firewall or cloud provider, Let's Encrypt certificate issuance will fail silently. Double-check before proceeding.
</Notice>

<Notice type="info" title="Don't want to self-host?">
Dokploy Cloud is available starting at $4.50/mo per server if you'd rather skip managing the infrastructure yourself.
</Notice>

## Flowise example: the Docker Compose file we'll deploy

Here's the compose file we'll use throughout this guide. It runs Flowise AI with a PostgreSQL backend. This is a **clean version**: no Traefik labels and no `dokploy-network`. Dokploy handles networking and routing through the Domains tab or Isolated Deployments.

```yml
services:
  flowise-db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - flowise-db-data:/var/lib/postgresql/data
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 5s
      timeout: 5s
      retries: 5

  flowise:
    image: flowiseai/flowise:latest
    healthcheck:
      test: wget --no-verbose --tries=1 --spider http://localhost:${PORT}
    volumes:
      - flowiseai:/root/.flowise
    environment:
      DEBUG: false
      PORT: ${PORT}
      FLOWISE_USERNAME: ${FLOWISE_USERNAME}
      FLOWISE_PASSWORD: ${FLOWISE_PASSWORD}
      APIKEY_PATH: /root/.flowise
      SECRETKEY_PATH: /root/.flowise
      LOG_LEVEL: info
      LOG_PATH: /root/.flowise/logs
      DATABASE_TYPE: postgres
      DATABASE_PORT: 5432
      DATABASE_HOST: flowise-db
      DATABASE_NAME: ${POSTGRES_DB}
      DATABASE_USER: ${POSTGRES_USER}
      DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
    restart: on-failure:5
    depends_on:
      flowise-db:
        condition: service_healthy
    entrypoint: /bin/sh -c "sleep 3; flowise start"

volumes:
  flowiseai:
    driver: local
  flowise-db-data:
    driver: local
```

A few things to note:

- **No `networks:` section**. Dokploy handles networking for you (via the Domains tab or Isolated Deployments).
- **No `labels:` block**. Traefik routing is configured through the Domains tab, not in the compose file.
- **No `container_name`**. Setting this in Dokploy causes issues with logs and metrics. Dokploy generates its own container names.
- **No `version:` key**. Docker Compose v2 ignores it and may warn about it. If you're bringing your own compose file with `version: "3.8"`, just remove that line.
- **`${VAR_NAME}` syntax**. Environment variables are referenced this way because Dokploy writes UI-defined vars to a `.env` file. More on this in the [environment variables section](#configuring-environment-variables-in-dokploy).

For a refresher on compose syntax, see [essential Docker commands](/docker-commands/). The official docs have more examples at the [Dokploy Docker Compose docs](https://docs.dokploy.com/docs/core/docker-compose) and the [official example](https://docs.dokploy.com/docs/core/docker-compose/example).

## Point the domain to your server (DNS)

Before deploying, point your domain or subdomain to the Dokploy server:

- Add an A record in your DNS settings:
  - **Name**: the subdomain you want (e.g., `flowise` for `flowise.yourdomain.com`)
  - **Value**: your server's IP address

<Picture src={img1} alt="DNS A Record" />

<Notice type="info" title="DNS validation">
Since Dokploy v0.22.0, the Domains tab includes built-in DNS validation. It checks whether your domain is correctly pointing to your server before you deploy.
</Notice>

## Method 1: using the Domains tab (recommended)

This is Dokploy's recommended approach. You paste your compose file as-is, then configure the domain and port through the UI. Dokploy injects the Traefik labels and networking config automatically.

For details on how Traefik works under the hood, see [how Traefik works as a reverse proxy in Docker](/traefik-proxy-docker/).

### Step 1: create the compose service

1. In the Dokploy dashboard, go to **Projects** and either create a new project or select an existing one.
2. Click **Create Service** → **Compose**.
3. Give it a name (e.g., `flowise`).
4. In **General**, select **Raw** as the source.
5. Paste the clean compose file from above.
6. Click **Save**.

<Picture src={img2} alt="Dokploy Compose" />

### Step 2: add a domain

1. Go to the **Domains** tab.
2. Click **Add Domain**.
3. Fill in the fields:
   - **Domain**: enter your domain (e.g., `flowise.yourdomain.com`)
   - **Service Name**: select the service from the dropdown (e.g., `flowise`. This is the key from your compose file)
   - **Internal Port**: the port your app listens on inside the container (3000 for Flowise)
   - **Certificate Provider**: select **Let's Encrypt** for automatic HTTPS
4. Click **Save**.

<Picture src={img4} alt="Dokploy Domain add" />

Dokploy will inject the required Traefik labels into the final compose file. You don't need to add them manually.

### Step 3: verify with Preview Compose

After configuring your domain, click the **Preview Compose** button. This shows you the final compose file Dokploy will actually execute, with all injected Traefik labels, network config, and domain settings. This is a critical verification step. Check that:

- The Traefik labels include your domain and the correct port
- The `dokploy-network` has been injected (or an isolated network if you enabled that)
- No unexpected changes were made to your services

<Notice type="success" title="Ready to deploy">
If Preview Compose shows the Traefik labels and your domain correctly, you're ready to deploy.
</Notice>

## Method 2: manual Traefik labels (advanced)

For most users, Method 1 is sufficient. Use manual Traefik labels only when you need custom configuration: middleware (auth, rate limiting), multiple host rules, custom entrypoints, or non-standard TLS config.

### When to use manual labels

- You need Traefik middleware (basic auth, rate limiting, custom headers)
- You're routing multiple domains to a single service
- You need custom entrypoints or TLS settings beyond what the Domains tab offers

If none of these apply, stick with the Domains tab.

### Adding dokploy-network and Traefik labels

With this approach, you modify the compose file to include the `dokploy-network` and Traefik labels:

```yml
services:
  flowise:
    image: flowiseai/flowise:latest
    networks:
      - dokploy-network
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.flowiseai.rule=Host(`flowise.yourdomain.com`)"
      - "traefik.http.routers.flowiseai.entrypoints=websecure"
      - "traefik.http.routers.flowiseai.tls.certResolver=letsencrypt"
      - "traefik.http.services.flowiseai.loadbalancer.server.port=3000"
    # ... rest of your service config

networks:
  dokploy-network:
    external: true
```

The Traefik label format:

```yml
labels:
  - "traefik.enable=true"
  - "traefik.http.routers.<unique-name>.entrypoints=websecure"
  - "traefik.http.routers.<unique-name>.tls.certResolver=letsencrypt"
  - "traefik.http.routers.<unique-name>.rule=Host(`app.yourdomain.com`)"
  - "traefik.http.services.<unique-name>.loadbalancer.server.port=3000"
```

Replace `<unique-name>` with a unique identifier for your service, set the `Host()` to your domain, and set the port to whatever your app listens on internally.

Every service that needs to communicate (e.g., the app and its database) must be on `dokploy-network`. For more on Traefik, see [Traefik FREE Let's Encrypt Wildcard Certificate](/traefik-wildcard-certificate/).

## Isolated Deployments (Advanced tab)

Dokploy's Isolated Deployments feature is a third option for networking. When enabled, Dokploy:

- Creates a **per-app isolated network** automatically
- Connects Traefik to that isolated network
- Eliminates the need to manually add `dokploy-network` to every service
- Prevents service name conflicts when running multiple instances of the same app

To enable it: go to the **Advanced** tab for your compose service and toggle **Isolated Deployments**.

<Notice type="info" title="Zero modifications needed">
When using Isolated Deployments together with the Domains tab, your compose file can be used as-is. No `networks:` section, no labels, no modifications at all. Just paste and deploy.
</Notice>

This is available for both Raw and Git provider compose services. See the [Utilities docs](https://docs.dokploy.com/docs/core/docker-compose/utilities) for details.

## Configuring Environment Variables in Dokploy

The Flowise example uses `${VAR_NAME}` syntax throughout. Here's how that works in Dokploy.

### Understanding the .env file mechanism

When you set environment variables in Dokploy's **Environment** tab, they are written to a `.env` file on the server. Your compose file must reference them with `${VAR_NAME}` syntax (or use `env_file: - .env`). This is different from application services, where vars are injected directly into containers.

For the Flowise example, add these in the **Environment** tab:

```sh
PORT=3000
POSTGRES_USER='user'
POSTGRES_PASSWORD='pass'
POSTGRES_DB='flowise'
FLOWISE_USERNAME=bitdoze
FLOWISE_PASSWORD=bitdoze
```

<Picture src={img3} alt="Dokploy env" />

<Notice type="warning" title="Variables are not auto-injected">
Environment variables set in the UI are NOT automatically injected into containers. You must reference them as `${VAR_NAME}` in your compose file or add `env_file: - .env` to your service definition.
</Notice>

A few additional notes:

- **Encryption at rest**: since v0.29.12, all environment variables are encrypted using AES-256-GCM.
- **Shared variables**: Dokploy supports project-level (`${{project.VAR_NAME}}`) and environment-level (`${{environment.VAR_NAME}}`) shared variables. Useful when multiple services in the same project share credentials. See the [Variables docs](https://docs.dokploy.com/docs/core/variables).

For a deeper dive on Docker environment variables, see [how Docker environment variables work with ARG and ENV](/docker-env-vars/).

## Deploy and verify your app

After configuring your compose file, environment variables, and domain, it's time to deploy:

1. Go to **General** and click **Deploy**.
2. Switch to the **Deployments** tab to watch the build logs.
3. Wait for a "success" status.

### Checking deployment logs

The Deployments tab shows real-time logs. A successful deploy ends with the containers running and Traefik routing traffic. If you see errors, the logs will point to the specific issue (wrong image tag, missing env var, port conflict, etc.).

### Verifying the app is running

Once the deploy succeeds, verify it:

```sh
curl -I https://flowise.yourdomain.com
```

You should get an `HTTP/2 200` response. The SSL certificate should be valid. Traefik issues it automatically via Let's Encrypt.

<Notice type="info" title="SSL timing">
It can take up to 10 seconds for Traefik to issue a Let's Encrypt certificate after the first deployment. If you get a certificate error immediately, wait a moment and try again.
</Notice>

## Backups for Docker Compose Apps

Since v0.22.0 (May 2025), Dokploy supports database backups and volume backups for Docker Compose services. If you're running anything with persistent data, set this up early.

### Database backups (PostgreSQL)

The Flowise example uses PostgreSQL. Dokploy can back up PostgreSQL, MariaDB, MySQL, and MongoDB databases within Docker Compose services. You configure this in the service's backup settings and point it to an S3-compatible destination.

To set up Cloudflare R2 as your backup destination, see how to [configure Dokploy backups with Cloudflare R2](/dokploy-backups-cloudflare-r2/).

### Volume backups

Dokploy can back up Docker named volumes to S3-compatible storage. This covers the `flowiseai` and `flowise-db-data` volumes in our example.

<Notice type="warning" title="Named volumes only">
Volume backups only work with named volumes (like `flowiseai:`), NOT bind mounts (like `./data:/app/data`). If your compose file uses bind mounts, you'll need a different backup strategy.
</Notice>

See the [Volume Backups docs](https://docs.dokploy.com/docs/core/volume-backups) for configuration details.

## Troubleshooting common issues

<Accordion label="DNS not propagated" group="troubleshooting" expanded="true">
**Symptom**: browser shows "server not found" or Dokploy's DNS validation fails in the Domains tab.

**Cause**: the A record isn't set, or the TTL hasn't expired yet.

**Fix**: check your DNS record:
```sh
dig flowise.yourdomain.com +short
```
This should return your server's IP. If it doesn't, verify the A record in your DNS provider. DNS propagation can take a few minutes to a few hours depending on your provider.
</Accordion>

<Accordion label="Port mismatch (502 Bad Gateway)" group="troubleshooting">
**Symptom**: you get a 502 Bad Gateway or connection refused after deploying.

**Cause**: the port configured in the Domains tab (or Traefik labels) doesn't match the port the app actually listens on inside the container.

**Fix**: check your app's documentation for its default port. For Flowise, it's `3000` (controlled by the `PORT` env var). Make sure the **Internal Port** in the Domains tab matches.
</Accordion>

<Accordion label="Services can't communicate" group="troubleshooting">
**Symptom**: the app starts but can't connect to its database (e.g., Flowise can't reach PostgreSQL).

**Cause**: services are not on the same network.

**Fix**: if you're using the Domains tab without Isolated Deployments, make sure all services that need to communicate are on `dokploy-network`. Or enable **Isolated Deployments** in the Advanced tab. Dokploy handles networking for you. Also verify that the service name in your connection string matches the key in the compose file (e.g., `flowise-db`, not `localhost`).
</Accordion>

<Accordion label="Container name issues" group="troubleshooting">
**Symptom**: logs or metrics not showing in the Dokploy dashboard.

**Cause**: `container_name` is set in the compose file.

**Fix**: remove the `container_name` directive. Dokploy generates its own container names for tracking logs, metrics, and deployments. Setting it manually breaks this.
</Accordion>

For more debugging commands, see [essential Docker commands](/docker-commands/). To monitor your server after deployment, [set up server monitoring with Beszel and Uptime Kuma](/beszel-uptime-kuma/).

## Updating and maintaining your compose app

After the initial deployment, you'll eventually need to update images, change environment variables, or modify the compose file. Dokploy makes this straightforward. Edit the config and redeploy.

For the full update workflow (including how to handle image tags, rollbacks, and redeployments), see our guide on [updating your deployed Docker Compose apps in Dokploy](/dokploy-update-docker-compose/).

## Conclusions

Deploying a Docker Compose app in Dokploy comes down to two approaches:

1. **Domains tab (recommended)**: paste your compose file as-is, configure the domain and port in the UI, and Dokploy handles the rest. No label editing, no network config.
2. **Manual Traefik labels (advanced)**: for when you need custom routing, middleware, or non-standard TLS setup.

With Isolated Deployments enabled, you can take the cleanest path: zero modifications to your compose file. Paste, set environment variables, add a domain, and deploy.

Don't skip backups. Set up database backups for any stateful service and volume backups for persistent data. It takes five minutes and saves you from data loss.

<Button text="How to Update Docker Compose Apps in Dokploy" link="/dokploy-update-docker-compose/" variant="solid" color="blue" size="md" icon="arrow-right" />

> If you are interested in more self-hosted apps, you can [discover more self-hosted Docker containers](/docker-containers-home-server/) or check [toolhunt.net self hosted section](https://toolhunt.net/sh/).