POST /api/v1/projects
status.phase to Pending, and stores the project. The scheduler picks it up asynchronously and assigns it to a Ready node.
Request body
string
required
Must be
"caravanserai/v1".string
required
Must be
"Project".object
required
object
required
Show properties
Show properties
object[]
required
Ordered list of containers to run. At least one service is required, and every service must have a non-empty
image.Show services[]
Show services[]
string
required
Service name. Used as the DNS hostname inside the shared Docker bridge network, so services can reach each other by name.
string
required
Docker image reference, e.g.
"nginx:alpine" or "postgres:15".object[]
object[]
Named volumes shared across services. Reference a volume from a service by setting
volumeMounts[].name to the same value.object[]
HTTP ingress routing rules. Each rule maps a hostname to a backend service.
Show ingress[]
Show ingress[]
string
required
Unique name for this ingress rule within the project.
string
Hostname for the rule. If the value contains a dot it is used verbatim; otherwise the hostname is assembled as
{host}.{environment}.{baseDomain}.object
required
string
RFC 3339 timestamp. When set, the GC controller deletes the project after this time. Useful for ephemeral preview or review environments.
Response
Returns201 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
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"
}
]
}
}'
curl -X POST http://localhost:8080/api/v1/projects \
-H "Content-Type: application/json" \
-d '{
"apiVersion": "caravanserai/v1",
"kind": "Project",
"metadata": {
"name": "wordpress"
},
"spec": {
"services": [
{
"name": "db",
"image": "mysql:8",
"env": [
{ "name": "MYSQL_ROOT_PASSWORD", "value": "secret" },
{ "name": "MYSQL_DATABASE", "value": "wp" }
],
"volumeMounts": [
{ "name": "mysql-data", "mountPath": "/var/lib/mysql" }
]
},
{
"name": "app",
"image": "wordpress:latest",
"env": [
{ "name": "WORDPRESS_DB_HOST", "value": "db" },
{ "name": "WORDPRESS_DB_PASSWORD", "value": "secret" },
{ "name": "WORDPRESS_DB_NAME", "value": "wp" }
]
}
],
"volumes": [
{ "name": "mysql-data", "type": "Ephemeral" }
]
}
}'
{
"apiVersion": "caravanserai/v1",
"kind": "Project",
"metadata": {
"name": "nginx-demo",
"createdAt": "2025-04-06T12:00:00Z",
"updatedAt": "2025-04-06T12:00:00Z"
},
"spec": {
"services": [
{
"name": "web",
"image": "nginx:alpine"
}
]
},
"status": {
"phase": "Pending"
}
}
{
"title": "Conflict",
"detail": "project already exists: nginx-demo",
"status": 409,
"type": "https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409"
}