Skip to main content
A Project is the core workload unit in Caravanserai. It groups one or more containers (services) that run together on a single node, sharing a Docker bridge network. Service names resolve as hostnames inside that network, identical to Docker Compose.
1

Write a project manifest

Create a YAML file that describes your workload. At minimum you need apiVersion, kind, metadata.name, and at least one service under spec.services.
project.yaml
The fields you can set in spec:

Multi-service example: WordPress

The following manifest runs MySQL and WordPress together. The app service reaches the database using the hostname db — resolved automatically over the shared bridge network.
wordpress.yaml
Ephemeral is currently the only supported volume type. Ephemeral volumes are discarded when the project is stopped or moved to a different node.
2

Apply the manifest

Run caractrl apply to send the manifest to the control plane. The API server creates the project record and sets its initial phase to Pending.
You should see output like:
3

Watch the phase change

Poll the project status to track its progress through the lifecycle:
Example output:
The PHASE column reflects the current lifecycle state:
Filter the project list by phase to focus on what matters:
Valid values for --phase are: Pending, Scheduled, Running, Failed, Terminating.
4

Check conditions if something goes wrong

If your project stays in Pending or moves to Failed, inspect the conditions array for a detailed reason. Use --output json to see the full status object:
Look for the status.conditions array in the output:
Each condition has a type, status (True / False), a machine-readable reason, and a human-readable message.
5

View the assigned node

Once the scheduler picks a node, status.nodeRef is populated with the node name. You can see it in the JSON output:
Use the node name to inspect that node’s capacity and health:
6

Clean up

Delete a project when you no longer need it. The control plane transitions the project to Terminating and signals the agent to tear down all containers, networks, and volumes.
If the agent acknowledges the teardown synchronously, you see:
Otherwise you see:
In either case, the project disappears from the list once the agent sets the phase to Terminated and the termination controller removes the record.

Common issues

The scheduler only assigns work to nodes in the Ready state. If no ready nodes exist in the cluster, the project waits indefinitely.Check your node states:
If all nodes are NotReady, verify that cara-agent is running on at least one machine and that its heartbeats are reaching the control plane. See Node management for details.
A Failed phase means the agent encountered a terminal error it cannot recover from on its own. The most common causes are:
  • Image pull failure — the image name or tag is wrong, or the registry is unreachable from the node.
  • Port conflict — another container on the node already occupies a required host port.
  • Volume error — the Docker daemon could not create the volume.
Retrieve the full condition list to find the specific reason:
Fix the underlying issue, then delete and re-apply the project — there is no in-place restart.

Ephemeral environments with spec.expireAt

Set spec.expireAt to an RFC 3339 timestamp to create a project that the GC controller automatically deletes after that time. This is useful for preview or CI environments that should not outlast a fixed window.
Once the timestamp passes, the GC controller deletes the project exactly as if you had run caractrl delete project pr-preview-42.