Creates a new project and submits it for scheduling.
The server validates the request, sets status.phase to Pending, and stores the project. The scheduler picks it up asynchronously and assigns it to a Ready node.
Request body
Must be "caravanserai/v1".
Unique name for the project. Must be a valid DNS label (lowercase alphanumeric and hyphens, no leading/trailing hyphens).
Arbitrary key/value string pairs for selection and grouping.
Non-identifying key/value metadata.
Ordered list of containers to run. At least one service is required, and every service must have a non-empty image. Service name. Used as the DNS hostname inside the shared Docker bridge network, so services can reach each other by name.
Docker image reference, e.g. "nginx:alpine" or "postgres:15".
Environment variables injected into the container at runtime. Volumes to mount into this container. Name of a volume defined in spec.volumes.
Absolute path inside the container where the volume is mounted.
Named volumes shared across services. Reference a volume from a service by setting volumeMounts[].name to the same value. Unique volume name within the project.
Volume lifecycle type. Currently only Ephemeral is supported — the volume is discarded when the project is stopped or moved to another node.
HTTP ingress routing rules. Each rule maps a hostname to a backend service. Unique name for this ingress rule within the project.
Hostname for the rule. If the value contains a dot it is used verbatim; otherwise the hostname is assembled as {host}.{environment}.{baseDomain}.
Name of the service to route traffic to.
Port on the target service.
Currently only Internal is supported — the route is visible only on the Headscale overlay network.
RFC 3339 timestamp. When set, the GC controller deletes the project after this time. Useful for ephemeral preview or review environments.
Response
Returns 201 Created with the stored Project object. status.phase is always Pending at creation time.
Returns 409 Conflict if a project with the same name already exists.
Examples
Minimal nginx project
Multi-service with volume
201 response
409 response
curl -X POST http://localhost:8080/api/v1/projects \
-H "Content-Type: application/json" \
-d '{
"apiVersion": "caravanserai/v1",
"kind": "Project",
"metadata": {
"name": "nginx-demo"
},
"spec": {
"services": [
{
"name": "web",
"image": "nginx:alpine"
}
]
}
}'