cpln convert command transforms Kubernetes manifests into Control Plane resources, enabling seamless migration from Kubernetes environments.
When to use this
Kubernetes migration
Migrate existing K8s workloads, secrets, and storage to Control Plane
Reuse existing manifests
Leverage your existing Kubernetes YAML files without rewriting them
Preview conversions
See how K8s resources translate to Control Plane before applying
CI/CD integration
Convert and apply K8s manifests in automated pipelines
Supported resources
Workload conversions
| Kubernetes Kind | Control Plane Kind | Notes |
|---|---|---|
| Deployment | Workload | Type determined by spec analysis |
| ReplicaSet | Workload | Type determined by spec analysis |
| ReplicationController | Workload | Legacy K8s resource |
| StatefulSet | Workload | Type determined by spec analysis |
| DaemonSet | Workload | Type determined by spec analysis |
| Job | Workload (cron) | Converted to cron with default schedule |
| CronJob | Workload (cron) | Schedule preserved from spec |
How workload type is determined
The converter analyzes your Kubernetes spec to automatically select the appropriate Control Plane workload type:Cron
Cron
Job and CronJob resources always become
cron workloads.- CronJobs preserve their schedule from
spec.schedule - Jobs use a default schedule of
* * * * *(every minute)
Stateful (highest priority)
Stateful (highest priority)
The workload becomes
stateful if any container mounts a volumeset (from PersistentVolumeClaim or volumeClaimTemplates).Stateful takes precedence over standard when both conditions are met.Standard
Standard
The workload becomes
standard if any of these conditions are true:- A container has no ports or more than one port
- A container uses gRPC health probes (liveness or readiness)
- The workload has rollout options (from K8s
strategy,updateStrategy,minReadySeconds, orpodManagementPolicy)
Serverless (default)
Serverless (default)
If none of the above conditions apply, the workload becomes
serverless. This is the default for simple deployments with a single HTTP port.Secret and config conversions
| Kubernetes Kind | Control Plane Kind | Notes |
|---|---|---|
| Secret | Secret | Direct conversion |
| ConfigMap | Secret (dictionary) | Stored as dictionary secret |
Storage conversions
| Kubernetes Kind | Control Plane Kind | Notes |
|---|---|---|
| PersistentVolumeClaim | Volumeset | Persistent storage |
Domain conversions
| Kubernetes Kind | Control Plane Kind | Notes |
|---|---|---|
| Ingress | Domain | Routes converted from Ingress rules |
How secrets are converted
Kubernetes Secrets are converted based on theirtype field:
| K8s Secret Type | Control Plane Secret Type |
|---|---|
kubernetes.io/dockerconfigjson | docker |
kubernetes.io/basic-auth | userpass |
kubernetes.io/tls | tls |
With payload key | opaque |
| Default | dictionary |
How volumesets are configured
PersistentVolumeClaims are converted to volumesets with these settings:| Property | Source (in priority order) | Default |
|---|---|---|
| Capacity | PersistentVolume spec.capacity.storage → PVC spec.resources.requests.storage | 10 GB |
| Performance class | StorageClass parameters (see below) | general-purpose-ssd |
| File system type | PV volume source fsType → StorageClass parameters.fsType | ext4 |
parameters contain any of these values, the volumeset uses high-throughput-ssd:
- AWS:
io1,io2 - GCP:
pd-extreme - Azure:
UltraSSD_LRS - VMware:
thick - Other:
fast,persistent_1
How domains are converted
Kubernetes Ingresses are converted to Control Plane Domains. The converter maps Ingress rules (host + paths) to domain routes, resolving backend Services to workloads.Field mapping
| Kubernetes Ingress | Control Plane Domain | Notes |
|---|---|---|
metadata.labels | tags | Labels preserved as domain tags |
spec.rules[].host | name | Wildcard prefix (*.) stripped for domain name |
spec.rules[].host (wildcard) | spec.acceptAllSubdomains | Set to true when host starts with *. |
spec.rules[].http.paths[].path | Route prefix or regex | Depends on pathType |
spec.rules[].http.paths[].backend.service | Route workloadLink | Resolved via Service selector → workload pod labels |
Service targetPort | Route port | Container port resolved through the Service |
Path type handling
Kubernetes pathType | Control Plane Route | Example |
|---|---|---|
Prefix | prefix: "/api" | Default behavior |
Exact | regex: "^/api/v1/users$" | Special characters escaped, anchored with ^ and $ |
ImplementationSpecific | prefix: "/legacy" | Treated as Prefix |
Service-to-workload resolution
The converter resolves each Ingress backend to a Control Plane workload through the following steps:Find the Service
The converter locates the Kubernetes Service referenced by the Ingress backend’s
service.name.Match the workload
The Service’s
selector labels are matched against workload pod template labels to find the target workload.Domain defaults
All converted domains use these fixed settings:| Property | Value |
|---|---|
| Port | 443 |
| Protocol | http2 |
| DNS mode | cname |
| Certificate challenge | http01 |
Wildcard and apex domain merging
When an Ingress contains rules for bothexample.com and *.example.com, the converter merges them into a single domain named example.com with acceptAllSubdomains: true. Routes from both rules are combined and deduplicated.
GVC context
If a--gvc is provided, workload links use it directly (e.g., //gvc/my-gvc/workload/api). Otherwise, the converter inserts a {{GVC}} placeholder that you must replace before applying.
Auto-generated resources
When workloads reference secrets (via environment variables or volume mounts), the converter automatically creates:- Identity - Named
identity-{workload-name}, linked to the workload - Policy - Named
policy-{workload-name}, grantingrevealpermission on referenced secrets
Firewall and public exposure
The converter analyzes your Kubernetes Services and Ingresses to determine if a workload should be publicly accessible:| Kubernetes Configuration | Control Plane Firewall |
|---|---|
Service type LoadBalancer matching pod labels | External inbound allowed (0.0.0.0/0) |
| Ingress routing to a Service matching pod labels | External inbound allowed (0.0.0.0/0) |
| No public exposure detected | External inbound blocked (default) |
All workloads have external outbound traffic allowed by default (
0.0.0.0/0).Informational resources
These Kubernetes resources are not directly converted but inform the conversion:| Resource | How it’s used |
|---|---|
| HorizontalPodAutoscaler | Sets minScale, maxScale, scaleToZeroDelay, and CPU target |
| Service | Protocol inference, public exposure detection, domain route resolution |
| ServiceAccount | Image pull secrets extraction |
| EndpointSlice | Service-to-Pod mapping for selectorless services |
| PersistentVolume | Capacity and file system type for volumesets |
| StorageClass | Performance class and file system type for volumesets |
Basic usage
Options
| Option | Description |
|---|---|
--file | Path to K8s JSON/YAML file. Use --file - for stdin |
--protocol | Override port protocol for all containers: http, http2, grpc, tcp |
--verbose | Show original K8s resources with ignored properties highlighted |
How port protocol is inferred
When you don’t specify--protocol, the converter automatically infers the protocol for each container port using a multi-level strategy (in priority order):
Service appProtocol (highest priority)
If a Kubernetes Service explicitly declares
appProtocol on a port that targets this container, that protocol is used.Service port name prefix
The converter checks if any Service port targeting this container has a name with a protocol prefix.
Health probe type
If the port has a liveness or readiness probe, the probe type determines the protocol:
grpcprobe → gRPChttpGetprobe → HTTPtcpSocketprobe → TCP
Recognized protocol prefixes
Port names starting with these prefixes are automatically mapped:| Prefix | Protocol |
|---|---|
http, https, ws, wss | HTTP |
http2, h2, h2c | HTTP/2 |
grpc, grpc-web, grpcweb | gRPC |
Well-known port mappings
These port numbers are automatically assigned protocols when no other signal is available:- HTTP ports
- gRPC ports
| Port | Protocol |
|---|---|
| 80, 81, 443 | HTTP |
| 3000, 3001, 3002 | HTTP |
| 8000, 8008, 8080, 8081, 8088 | HTTP |
| 8443, 9090 | HTTP |
If no protocol can be inferred, the converter defaults to
tcp.Example conversion
More conversion examples
- Serverless (default)
- Standard
- Stateful
- Cron
- ConfigMap
- Domain (Ingress)
A simple Deployment with one HTTP port becomes serverless:Converts to a serverless workload (single port, no volumes, no gRPC probes).
k8s-deployment.yaml
Override protocol inference
To bypass automatic protocol inference and set a specific protocol for all ports, use the--protocol flag:
Verbose mode
Use--verbose to see which K8s properties were converted and which were ignored:
(ignored).
Delete converted resources
To remove resources that were applied from a K8s manifest:Troubleshooting
Resource type not supported
Resource type not supported
Some Kubernetes resources don’t have direct Control Plane equivalents. Use
--verbose to see which properties are ignored during conversion.ConfigMap values not appearing
ConfigMap values not appearing
ConfigMaps are converted to dictionary secrets. Reference them in your workload using environment variables or volume mounts pointing to the secret.
HPA settings not applied
HPA settings not applied
HorizontalPodAutoscaler settings inform the workload’s autoscaling configuration. Check the converted workload’s
defaultOptions.autoscaling section.Service ports not exposed
Service ports not exposed
Services are used to inform port mappings but don’t create standalone resources. Ports are configured directly on the workload’s container spec.
PVC not creating volumeset
PVC not creating volumeset
PersistentVolumeClaims are converted to volumesets. Ensure the PVC is referenced by a StatefulSet’s volumeClaimTemplates for proper conversion.
Ingress not creating a domain
Ingress not creating a domain
Ingress conversion requires the referenced Services and their matching workloads (Deployments, StatefulSets, etc.) to be present in the same file. The converter uses the Service selector to find the target workload by matching pod template labels.
Service without selector error
Service without selector error
Ingress backend Services must have a
selector defined so the converter can resolve which workload the Service targets. Services without selectors (e.g., ExternalName services) are not supported for domain conversion.Workload link shows {{GVC}} placeholder
Workload link shows {{GVC}} placeholder
When converting Ingresses without specifying
--gvc, workload links contain a {{GVC}} placeholder. Provide --gvc during conversion or replace the placeholder before applying.