Deploy Any Docker Compose App with Dokploy
24 Oct 2025If 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:
- Service Name:
app(the key from the compose file) - Container Port:
3000 - Host:
app.example.com
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:
depends_ondoes not wait for readiness. It barely waits at all. Your app must retry its database connection.restart: alwaysis replaced bydeploy.restart_policy.build:works, but builds on the manager node. For multi-server setups, push to a registry instead.container_nameis ignored — Swarm names containers itself.
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.