> ## Documentation Index
> Fetch the complete documentation index at: https://docs.controlplane.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy Docker Compose Projects

> Convert and deploy Docker Compose projects to Control Plane with the cpln stack command.

The `cpln stack` command deploys Docker Compose projects directly to Control Plane, automatically converting services, volumes, secrets, and networks to their Control Plane equivalents.

## When to use this

<CardGroup cols={2}>
  <Card title="Migrate from Docker Compose" icon="docker">
    Move existing Compose projects to Control Plane without rewriting configuration
  </Card>

  <Card title="Local-to-cloud workflow" icon="cloud-arrow-up">
    Develop locally with Compose, deploy to Control Plane for production
  </Card>

  <Card title="Multi-service apps" icon="cubes">
    Deploy interconnected services as a cohesive stack
  </Card>

  <Card title="Preview deployments" icon="eye">
    Generate and inspect Control Plane manifests before deploying
  </Card>
</CardGroup>

## Prerequisites

<AccordionGroup>
  <Accordion title="CLI installed">
    Install the Control Plane CLI. See [Installation](/cli-reference/installation).
  </Accordion>

  <Accordion title="Docker Compose project">
    You need a `docker-compose.yml` or `compose.yaml` file.
  </Accordion>

  <Accordion title="Required permissions">
    You need permissions to create workloads, secrets, volumesets, identities, and push images.
  </Accordion>
</AccordionGroup>

## Deploy a project

```bash theme={null}
cpln stack deploy
```

This reads your Compose file and deploys all services to Control Plane.

See the [stack deploy](/cli-reference/commands/stack#stack-deploy) command reference for all options.

## Delete a project

Remove all resources created by a Compose deployment:

```bash theme={null}
cpln stack rm
```

See the [stack rm](/cli-reference/commands/stack#stack-rm) command reference for details.

## Preview the generated manifests

Generate Control Plane specs without deploying:

```bash theme={null}
cpln stack manifest
```

This outputs the converted YAML so you can inspect or modify it before deployment.

## Customize workloads with x-cpln

Add an `x-cpln` block to any service to override the generated workload spec. Each top-level key in `x-cpln` **replaces** the corresponding section in the workload spec:

```yaml docker-compose.yml theme={null}
services:
  api:
    image: ghcr.io/example/api:1.2.3
    ports:
      - '8080:8080'

    x-cpln:
      type: standard                    # Override workload type
      containers:                        # Replace entire containers array
        - name: api
          cpu: 250m
          memory: 256Mi
      defaultOptions:                    # Replace defaultOptions
        capacityAI: false
        autoscaling:
          minScale: 0
          maxScale: 3
          metric: concurrency
          target: 50
      firewallConfig:                    # Replace firewallConfig
        external:
          inboundAllowCIDR:
            - 0.0.0.0/0
        internal:
          inboundAllowType: same-gvc
```

<Warning>
  The `x-cpln` block **replaces** entire spec sections, and doesn't merge them. If you override `containers`, you must include all container configuration.
</Warning>

### Available overrides

| Key                  | Description                                         |
| -------------------- | --------------------------------------------------- |
| `type`               | Workload type: `serverless`, `standard`, `stateful` |
| `containers`         | Complete container specifications                   |
| `defaultOptions`     | Autoscaling, capacity AI, timeouts, suspend         |
| `firewallConfig`     | External and internal firewall rules                |
| `identityLink`       | Link to a specific identity                         |
| `supportDynamicTags` | Enable dynamic image tag detection                  |
| `loadBalancer`       | Load balancer configuration                         |
| `rolloutOptions`     | Deployment rollout strategy                         |
| `securityOptions`    | Security context settings                           |
| `localOptions`       | Location-specific overrides                         |

## Service-to-service communication

Update service URLs to use the Control Plane local syntax:

```diff theme={null}
- http://service2:8080
+ http://service2.{GVC}.cpln.local:8080
```

Replace `{GVC}` with your actual GVC name.

<Info>
  See the [Service-to-Service](/guides/service-to-service#service-endpoint-syntax) guide for the full endpoint syntax.
</Info>

## How workload type is determined

The converter analyzes your service definition to select the appropriate workload type:

| Condition                    | Workload Type |
| ---------------------------- | ------------- |
| Service has volumes attached | `stateful`    |
| No volumes attached          | `standard`    |

<Tip>
  Use `x-cpln` to override the automatically determined type if needed.
</Tip>

## Translation reference

| Compose Feature  | Control Plane Resource                                                      |
| ---------------- | --------------------------------------------------------------------------- |
| Services         | Single-container workloads                                                  |
| Networks         | Internal firewall configuration                                             |
| Named volumes    | Volumesets (10GB, ext4, general-purpose-ssd)                                |
| Secrets          | [Opaque secrets](/reference/secret#opaque) + policies + identities          |
| Configs          | [Opaque secrets](/reference/secret#opaque) (treated identically to secrets) |
| File bind mounts | [Opaque secrets](/reference/secret#opaque)                                  |

### Resource mapping

| Compose Field                    | Control Plane Equivalent                                                                                          |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| `deploy.resources.limits.cpus`   | Container CPU (multiplied by 1000 for millicores)                                                                 |
| `deploy.resources.limits.memory` | Container memory                                                                                                  |
| `deploy.replicas`                | `minScale` and `maxScale` (set to same value)                                                                     |
| `healthcheck`                    | Readiness probe                                                                                                   |
| `ports` / `expose`               | Container ports                                                                                                   |
| `environment` / `env_file`       | Environment variables (`environment` processed first; `env_file` entries added only for keys not already defined) |
| `working_dir`                    | Container working directory                                                                                       |
| `command`                        | Container args                                                                                                    |
| `entrypoint`                     | Container command                                                                                                 |

### Port protocol

Specify the protocol directly in the port string:

```yaml theme={null}
ports:
  - "8080:80/http"      # HTTP protocol
  - "50051:50051/grpc"  # gRPC protocol
  - "9000:9000/http2"   # HTTP/2 protocol
  - "5432:5432/tcp"     # TCP protocol
  - "3000:3000"         # No protocol set if not specified
```

### Defaults

| Property          | Default Value                                                     |
| ----------------- | ----------------------------------------------------------------- |
| CPU               | `42m`                                                             |
| Memory            | `128Mi`                                                           |
| External inbound  | Allowed if `ports` defined or `network_mode: host`                |
| External outbound | Allowed (unless `network_mode: none`)                             |
| Capacity AI       | Enabled only if: reservations \< limits, no GPU, and not stateful |

### Secrets and configs

Both `secrets` and `configs` are converted to Control Plane secrets:

```yaml theme={null}
services:
  api:
    image: myapp:latest
    secrets:
      - db_password           # Simple reference
      - source: api_key       # With custom target path
        target: /app/api.key

secrets:
  db_password:
    file: ./secrets/db_password.txt
  api_key:
    file: ./secrets/api_key.txt
```

* Default mount path: `/run/secrets/{name}` (if no absolute path specified)
* Identities and policies are automatically created for workloads using secrets

### Healthcheck conversion

Docker healthchecks are converted to readiness probes:

| Compose Field  | Control Plane Field   |
| -------------- | --------------------- |
| `test`         | `exec.command`        |
| `interval`     | `periodSeconds`       |
| `timeout`      | `timeoutSeconds`      |
| `start_period` | `initialDelaySeconds` |
| `retries`      | `failureThreshold`    |

Supported test formats:

* String: `"curl http://localhost/health"` → `/bin/sh -c` wrapper
* CMD: `['CMD', 'curl', 'http://localhost']` → direct execution
* CMD-SHELL: `['CMD-SHELL', 'curl http://localhost']` → `/bin/sh -c` wrapper
* NONE: Disables the readiness probe

### Network modes

| Mode                          | Behavior                                          |
| ----------------------------- | ------------------------------------------------- |
| Default (no networks)         | All services can reach each other                 |
| Named networks                | Only services in the same network can communicate |
| `network_mode: host`          | External inbound traffic allowed                  |
| `network_mode: none`          | No outbound traffic allowed                       |
| `network_mode: service:other` | Shares network with another service               |

### GPU support

Services with GPU devices are automatically configured:

```yaml theme={null}
deploy:
  resources:
    reservations:
      devices:
        - capabilities: ['gpu']
          count: 1
```

GPU workloads receive:

* NVIDIA T4 GPU
* Minimum CPU: `2000m` (overrides default)
* Minimum Memory: `7168Mi` (overrides default)
* Capacity AI: Disabled

### Service inheritance

Extend services from the same or different compose files:

```yaml theme={null}
services:
  api:
    extends:
      service: base
      file: base-compose.yml
    environment:
      - API_KEY=secret  # Overrides parent
```

Child values take precedence over parent values.

## Limitations

<Warning>
  The following Docker Compose features are **not supported**:
</Warning>

| Feature                         | Status        | Alternative                                     |
| ------------------------------- | ------------- | ----------------------------------------------- |
| Directory bind mounts           | Not supported | Use named volumes or file bind mounts           |
| `depends_on` ordering           | Ignored       | Services start independently; use health checks |
| `links`                         | Ignored       | Use network-based service discovery             |
| Dynamic GPU model/quantity      | Limited       | Hardcoded to NVIDIA T4, quantity 1              |
| Multiple containers per service | Not supported | Create separate services                        |
| `privileged` mode               | Not supported | Use `securityOptions` via x-cpln                |

## Troubleshooting

<AccordionGroup>
  <Accordion title="Service-to-service connection fails">
    Update hostnames to use the Control Plane local syntax:

    ```
    http://service-name.{GVC}.cpln.local:{port}
    ```

    Ensure both services are in the same network or no networks are defined (global network).
  </Accordion>

  <Accordion title="Ports not accessible">
    Ensure all ports are explicitly listed in `ports` or `expose` in your Compose file. Only services with ports defined get external inbound access.
  </Accordion>

  <Accordion title="Secret not found">
    Secrets referenced in Compose are converted to Control Plane secrets. The converter automatically creates identities and policies. Check that:

    * The secret file exists at the specified path
    * You have permissions to create secrets, identities, and policies
  </Accordion>

  <Accordion title="Directory bind mount error">
    Directory bind mounts are not supported. Convert to either:

    * Named volumes (for persistent data)
    * File bind mounts (for configuration files → converted to secrets)
  </Accordion>

  <Accordion title="Both image and build specified">
    A service cannot have both `image` and `build` specified. Use one or the other:

    * `image`: Pull from a registry
    * `build`: Build and push to Control Plane registry
  </Accordion>

  <Accordion title="Services can't communicate">
    By default, all services can reach each other. If you're using named networks, ensure communicating services are in the same network.
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Apply YAML Manifests" href="/guides/cpln-apply" icon="file-code">
    Deploy resources from YAML files
  </Card>

  <Card title="Service-to-Service" href="/guides/service-to-service" icon="arrows-left-right">
    Configure internal networking
  </Card>

  <Card title="Workload Reference" href="/reference/workload/general" icon="server">
    Understand workload configuration
  </Card>

  <Card title="Stack Command Reference" href="/cli-reference/commands/stack" icon="book">
    Full stack command reference
  </Card>
</CardGroup>
