Mental model
vs. Docker Compose
A Project maps directly to a
docker-compose.yml file. Each ServiceDef
is a Compose service; each VolumeDef is a named volume. The key
difference is that Caravanserai schedules the entire project onto a cluster
node rather than running it locally.vs. Kubernetes
A Project is closest to a Kubernetes Pod (co-located containers, shared
network) combined with a Deployment (desired-state reconciliation).
Caravanserai does not scatter services across nodes — all services in a
project always land on the same node.
ProjectSpec fields
spec is the desired state you declare. The control plane and agent reconcile
the cluster toward it continuously.
services
A list ofServiceDef objects — the containers that make up the project.
Every project must have at least one service.
volumes
A list ofVolumeDef objects — named storage units that one or more services
can mount.
An
Ephemeral volume is created when the project starts and deleted when
the project is stopped or moved. It does not survive rescheduling. If you
need durable storage, manage the volume on the node directly and use a
host path (not yet supported in the API; track the roadmap for HostPath
support).ingress
A list ofIngressDef objects — HTTP routing rules that expose a service
through the cluster’s ingress layer.
expireAt
An optional RFC 3339 timestamp. When set, the garbage-collection controller deletes the project automatically after this time. Use this for ephemeral preview environments or time-boxed jobs.Project lifecycle
Every project moves through a defined set of phases. Phase transitions are driven by two actors: the scheduler (part ofcara-server) and the
agent running on the assigned node.
Conditions
status.conditions is a list of Condition objects that give you structured
detail about what happened at each phase transition. Two condition types are
relevant for projects:
Phase
Phase
Updated on every lifecycle phase transition.
status is always True;
the condition acts as a structured changelog rather than a health signal.
Read reason (a CamelCase word) and message (human-readable) to
understand why the project entered its current phase.NotReadyAt
NotReadyAt
Written once when the control plane first observes a
Running project on
a NotReady node. lastTransitionTime marks the start of the grace
period — after this window the control plane may intervene and reschedule
the project.TerminatingAt
TerminatingAt
Written once when the control plane first observes a
Terminating project
on a NotReady node. lastTransitionTime marks the start of the
force-termination timeout clock.Example: multi-service project
The following manifest deploys a WordPress application with a MySQL database. Theapp service references db by name — this works because both containers
share the project’s Docker bridge network, just like Docker Compose.
Running:
Docker resource naming
The agent uses deterministic names for every Docker resource it creates, so reconciliation remains stateless across restarts.
Every container also receives two labels: