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
Migrate from Docker Compose
Move existing Compose projects to Control Plane without rewriting configuration
Local-to-cloud workflow
Develop locally with Compose, deploy to Control Plane for production
Multi-service apps
Deploy interconnected services as a cohesive stack
Preview deployments
Generate and inspect Control Plane manifests before deploying
Prerequisites
CLI installed
CLI installed
Install the Control Plane CLI. See Installation.
Docker Compose project
Docker Compose project
You need a
docker-compose.yml or compose.yaml file.Required permissions
Required permissions
You need permissions to create workloads, secrets, volumesets, identities, and push images.
Deploy a project
Turn off an option that is on by default with its
--no- form, such as --no-deps.
See the stack deploy command reference for all options.
Delete a project
Remove all resources created by a Compose deployment:image: api:${VERSION} next to build, set the same variables and pass the same --env-file options you deployed with.
See the stack rm command reference for details.
Preview the generated manifests
Generate Control Plane specs without deploying:--build, and takes --env-file, --no-interpolate and --no-deps like cpln stack deploy.
Compose variables and env files
cpln stack substitutes variables the way docker compose does:
- The environment of the command.
- The
.envfile next to the compose file, or the files you name with--env-filein its place. Give--env-filemore than once to layer files, a later file overriding an earlier one.
Only values are substituted, never keys. The contents of
env_file files are used as written. Pass --no-interpolate to keep every value as written; --env-file cannot be combined with it.Service env files
env_file on a service is separate from the .env file above. It loads the variables of a file into the containers of that service, while .env and --env-file feed the substitution of the compose file itself. A missing .env file is fine; a missing env_file file is not.
Each path resolves against the directory of the compose file. When a file does not exist, the command stops before it builds or deploys anything, as docker compose up does, and names the file and every service that reads it. To skip a file while it is missing, list it in the long syntax with required: false:
common.env must exist. local.env is read when it exists and skipped when it does not.
When the same variable is set in more than one place, the order follows docker compose: a later env file in the list wins over an earlier one, and environment wins over every env file. A service that uses extends reads the env files it inherits first, so its own env files win over them.
Compose image names and tags
cpln stack deploy builds the image of every service that sets build and pushes it to your org’s registry:
The tag can come from a variable, so the same compose file tags images the same way locally and on Control Plane:
compose.yaml
- Next to
build,imagemust be a plainNAME:TAG. The image always goes to your org’s registry, so a registry host or a path, such asghcr.io/acme/api:1.4.2, is rejected. - Services that build the same image with the same build settings build it once. Building one image from different build settings is an error.
- A service with
imageonly that names an image another service builds, such asmigrationsabove, runs that image from your org’s registry. cpln stack manifestbuilds nothing unless you pass--build; the manifest references the images either way.
Compose service dependencies
cpln stack deploy follows depends_on conditions the way docker compose up does. Pass --no-deps to ignore them and apply every workload at once.
A service that another waits for with
service_completed_successfully, such as a database migration, converts to a cron workload that is suspended, so it never runs on a schedule. cpln stack deploy starts one execution in the first location of the GVC and waits for it to finish.
compose.yaml
Created and Updated. This lets each dependency’s internal firewall admit them from its first deploy.
--wait-timeoutsets the seconds each wait may take, 600 by default.- When a dependency never becomes ready or its job fails, the services that need it are left as they were, the rest of the stack still deploys, and the command exits with an error that names both.
- A dependency marked
required: falseis waited for, but never keeps its dependent from deploying. - A job serves no traffic, so its ports are dropped with a warning, and it cannot mount a named volume.
- A dependency on a service that is not in the file (unless it is
required: false), a cycle, or an unknown condition is an error, as in docker compose. So is waiting withservice_healthyfor a service that runs as a job.
Compose dependencies in manifests
cpln stack manifest converts jobs the same way, but a manifest cannot carry the order: cpln apply creates every workload at once and starts no job, and the command prints a warning saying so. Deploy with cpln stack deploy to keep the order, or start each job yourself after applying:
Customize workloads with x-cpln
Add anx-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:
docker-compose.yml
On a service that runs as a job,
x-cpln applies on top of the cron workload. Overridden defaultOptions keep the job suspended unless they set suspend themselves, and type must stay cron.Available overrides
Service-to-service communication
Update service URLs to use the Control Plane local syntax:{GVC} with your actual GVC name.
See the Service-to-Service guide for the full endpoint syntax.
How workload type is determined
The converter analyzes your service definition to select the appropriate workload type:Translation reference
Resource mapping
Port protocol
Specify the protocol directly in the port string:Defaults
Secrets and configs
Bothsecrets and configs are converted to Control Plane secrets:
- 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:
Supported test formats:
- String:
"curl http://localhost/health"→/bin/sh -cwrapper - CMD:
['CMD', 'curl', 'http://localhost']→ direct execution - CMD-SHELL:
['CMD-SHELL', 'curl http://localhost']→/bin/sh -cwrapper - NONE: Disables the readiness probe
h, m, s, ms and us, alone or combined such as 1m30s, and are rounded up to whole seconds. A duration without a unit, such as 30, is not a compose duration: it is ignored with a warning, and the platform default applies. A service that runs as a job gets no readiness probe.
Network modes
GPU support
Services with GPU devices are automatically configured:- 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:Limitations
Troubleshooting
Service-to-service connection fails
Service-to-service connection fails
Update hostnames to use the Control Plane local syntax:Ensure both services are in the same network or no networks are defined (global network).
Ports not accessible
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.Secret not found
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
The env file ... does not exist
The env file ... does not exist
The command stops before it builds or deploys anything, with an error such as:The
env_file of each service named in the error lists this file. Create the file, or list it with required: false as shown in Service env files. The .env file that variable substitution reads is optional and never causes this error.Directory bind mount error
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)
sets both build and image ... image must be a plain NAME:TAG
sets both build and image ... image must be a plain NAME:TAG
The command stops with an error such as:A service that sets both
image and build is built and tagged with the image name, and always pushed to your org’s registry, so image cannot name another registry or hold an unresolved variable. To fix it:- Remove a registry host or path, such as
ghcr.io/acme/, fromimage. - If
imageholds a variable, such asapi:${VERSION}, set it and do not pass--no-interpolate. An empty tag, such asapi:, means the variable is not set.
Workload type may not be changed
Workload type may not be changed
cpln stack deploy fails with Workload type may not be changed and prints a hint that names each rejected workload, such as:- For a service that becomes a job, pass
--no-deps. Every workload is then applied at once, with no jobs. - For any other service, undo the change that set the new type, such as an added or removed named volume, or an
x-cplntype.
cpln workload delete and deploying again, and the workload and everything that calls it are down in between. The CLI never deletes a workload for you.Workload was not deployed because a dependency did not complete or become ready
Workload was not deployed because a dependency did not complete or become ready
cpln stack deploy exits with errors such as:--wait-timeout.Services can't communicate
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.
Next steps
Apply YAML Manifests
Deploy resources from YAML files
Service-to-Service
Configure internal networking
Workload Reference
Understand workload configuration
Stack Command Reference
Full stack command reference