---
title: Import a Docker Compose file
description: Turn a docker-compose.yml into Dekka services, managed databases and connections, see a report of what changed, and deploy it.
section: Services
order: 4.6
updated: 2026-11-04
---

Dekka can read a `docker-compose.yml` and set up the same app as Dekka services. Containers that run your code become services, Postgres, Redis and MinIO containers become managed databases and buckets with [connections](/docs/connections), and the variables that pointed at those containers are rewritten to use the connections. A report lists what carried over, what changed to fit Dekka and what was left out, and nothing is created until you deploy the plan.

## Import in the dashboard

1. On a project's canvas, choose **Add**, then **Template or dekka.yaml**, and open the **Docker Compose** tab.
2. Paste the file or choose **Open a file**, then choose **Translate**.
3. Read the report. If any service has a `build` section, choose the GitHub **Repository** and **Branch** to build it from.
4. Fill in any values the file asks for, choose **Review changes**, check the planned changes and deploy them.

The translation is a [dekka.yaml blueprint](/docs/config-as-code#describe-a-whole-environment-in-dekkayaml), so you can also copy it, keep it in your repository and plan it again later.

## What the report looks like

This is part of n8n's own Compose file for running with Postgres.

```yaml title="docker-compose.yml"
services:
  postgres:
    image: postgres:16
    environment:
      - POSTGRES_USER
      - POSTGRES_PASSWORD
      - POSTGRES_DB
    volumes:
      - db_storage:/var/lib/postgresql/data

  n8n:
    image: docker.n8n.io/n8nio/n8n
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_USER=${POSTGRES_NON_ROOT_USER}
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD}
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY:?set an encryption key}
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE:-Africa/Accra}
    ports:
      - 5678:5678
    volumes:
      - n8n_storage:/home/node/.n8n
    depends_on:
      - postgres
```

The report groups each note under **Carried over as written**, **Changed to fit Dekka** or **Left out**.

| Service | Level | Note |
| --- | --- | --- |
| postgres | adjusted | Becomes a managed Postgres 16. Dekka creates the credentials and gives them to the services that use it. |
| postgres | applied | Its storage is managed, so the volume isn't needed. |
| n8n | adjusted | The image has no tag, so it uses docker.n8n.io/n8nio/n8n:latest. Pin a version to control upgrades. |
| n8n | applied | Served on port 5678 at a public dekka.cloud domain. |
| n8n | applied | Gets a 1 GB volume at /home/node/.n8n. Resize it in Dekka if it needs more. |
| n8n | adjusted | DB_POSTGRESDB_HOST now reads PGHOST from the connection. |
| n8n | adjusted | DB_POSTGRESDB_USER now reads PGUSER from the connection. |
| n8n | applied | Connected to postgres. |

The n8n service ends up with `DB_POSTGRESDB_HOST` set to `${{n8n.PGHOST}}`, so it reads the managed database's host from its connection, and `N8N_ENCRYPTION_KEY` becomes a required value you enter before planning.

## How Compose maps to Dekka

| In Compose | On Dekka |
| --- | --- |
| A service with `image` and `ports` | A web service running that image on the first published port, at a public address |
| A service with `image` and only `expose` | A web service on that port, reachable inside the environment only |
| A service with no ports | A [worker](/docs/service-types) |
| `build` | A service built from your GitHub repository with the [Dockerfile builder](/docs/builders#dockerfile), using `context` as the root directory and `dockerfile`, `target` and `args` as its settings |
| `postgres`, `postgis`, `pgvector` or `timescaledb` images | A managed [PostgreSQL](/docs/postgresql) 16 or 17, matching the image's major version |
| `redis`, `valkey` or `keydb` images | A managed [Redis](/docs/redis) with persistence on |
| A `minio` image | A Dekka [bucket](/docs/object-storage) |
| `depends_on`, `links`, or a variable naming a database container | A connection from the service to that database |
| `command` and `entrypoint` | The start command |
| `environment` | Service variables |
| The first `volumes` entry with a container path | A 1 GB [volume](/docs/volumes) at that path |
| `healthcheck` that requests `http://localhost/...` | The health check path |
| `deploy.resources.limits.memory` or `mem_limit` | Memory, rounded to a size Dekka offers |
| `deploy.resources.limits.cpus` or `cpus` | vCPU, in steps of 0.25 |
| `deploy.replicas` | Replicas, unless the service has a volume |
| `restart` | The restart policy, with `unless-stopped` treated as `always` |

## Variables that point at databases

Inside Compose, services find each other by container name, such as `postgres:5432`. On Dekka a connection gives the service its own credentials as [variables](/docs/connections#which-variables-does-a-connection-set), so the translation rewrites values that named a database container.

- A URL such as `DATABASE_URL=postgres://user:pass@db:5432/app` or `REDIS_URL=redis://cache:6379` is dropped, because the connection sets that variable itself.
- A variable whose value is the container's name, port, user, password or database, such as `DB_HOST=db` or `DB_PASSWORD=${POSTGRES_PASSWORD}`, reads the matching connection variable instead, such as `${{web.PGHOST}}` or `${{web.PGPASSWORD}}`.
- A URL or host variable that names another service, such as `API_URL=http://api:8000`, uses that service's private domain, `${{api.DEKKA_PRIVATE_DOMAIN}}`.
- A secret whose value is a placeholder, such as `APP_SECRET=replace-me-with-a-random-string`, gets a random value that Dekka generates once.
- Variables Dekka sets itself, such as `PORT`, keep Dekka's value.

## Values from your shell

Compose fills `${VAR}` from your shell or a `.env` file, which Dekka can't see, so the translation turns them into values you enter when you plan.

| In Compose | On Dekka |
| --- | --- |
| `${VAR:-default}` or `${VAR-default}` | The default, written into the variable |
| `${VAR}`, `$VAR` or a bare `VAR` in a list | An optional value you can enter, left unset if you don't |
| `${VAR:?message}` or `${VAR?message}` | A required value, with the message as its description |
| `$$` | A literal `$` |

Names that look like secrets, such as ones containing `PASSWORD`, `TOKEN` or `KEY`, are entered as secrets. Commands and build arguments can't read these values, so a command or build argument that uses one without a default is left out and the report says so.

## Services that build from source

A service with a `build` section is built from a GitHub repository you choose, with the Dekka GitHub App [connected](/docs/deploying-from-github). Every such service in the file uses the same repository and branch, and its `context` becomes its root directory. Planning without a repository answers `422 repository_required` with the services that need one. A remote build context, such as a Git URL, isn't fetched, and `dockerfile_inline` isn't read, so commit the Dockerfile to the repository instead.

## What's left out

| Compose setting | What to do instead |
| --- | --- |
| More than one published port | Dekka serves one port per service. Split the other ports into their own services. |
| UDP ports | Dekka serves HTTP and TCP only. |
| A second volume on a service | A service has one volume. Combine the paths under one directory. |
| `env_file` | Add those variables to the service in Dekka. |
| `secrets` and `configs` | Add secrets as secret variables, and put config files in the image or a volume. |
| `extends` | Copy the settings into the service. |
| `networks` and `network_mode` | Every service shares the environment's private network, so nothing is needed. |
| `profiles` | Every service is included. Remove the ones you don't want before importing. |
| `privileged`, `cap_add`, `devices`, `pid`, `ipc`, `sysctls` and `security_opt` | Services can't run with host-level access. |
| `working_dir` and `user` | Set them in the image's Dockerfile. |
| `extra_hosts`, `dns` and `tmpfs` | Not available. |
| A health check that isn't an HTTP request | Dekka checks `GET /` instead, or set a health check path in the service's settings. |

Settings that make no difference on Dekka, such as `container_name`, `labels`, `logging` and `stop_signal`, are ignored without a note.

## Import with the API

`POST /v1/compose/translate` returns the blueprint, the report, the resources it would create, the values it needs and whether it needs a repository, and changes nothing.

```bash tab="cURL"
curl https://api.dekka.cloud/v1/compose/translate \
  -H "Authorization: Bearer $DEKKA_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile content docker-compose.yml '{content: $content}')"
```

`POST /v1/environments/{id}/compose/plan` translates the file again and plans it into an environment, returning a [changeset](/docs/staged-changes). Pass the values it asks for in `inputs`, and the repository for any `build` services.

```bash tab="cURL"
curl https://api.dekka.cloud/v1/environments/$ENV_ID/compose/plan \
  -H "Authorization: Bearer $DEKKA_API_KEY" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile content docker-compose.yml '{
        content: $content,
        inputs: {N8N_ENCRYPTION_KEY: "your-32-character-key"},
        repository: {full_name: "acme/shop", branch: "main"}
      }')"
```

Deploy the changeset with its `id` once you've read the changes.

```bash tab="cURL"
curl -X POST https://api.dekka.cloud/v1/changesets/$CHANGESET_ID/deploy \
  -H "Authorization: Bearer $DEKKA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"confirm": true}'
```

A file that isn't valid YAML, has no `services` or is larger than 200 KB answers `422 invalid_compose`. A required value left out answers `422 missing_input` with the missing keys, and going over your plan's limits answers `403 entitlement_exceeded`.
