Skip to main content
A Project is the central workload resource in Caravanserai. It groups one or more containers — called services — that must run together on a single node. Services share a Docker bridge network and resolve each other by service name, exactly like Docker Compose.

Schema

Top-level fields

apiVersion
string
required
Must be caravanserai/v1.
kind
string
required
Must be Project.
metadata
object
required
Identity metadata for the resource.
metadata.name
string
required
Unique name for the Project within the cluster. Used to reference the Project in caractrl commands and as the basis for Docker resource names (cara-{name} for the network, {name}-{service} for containers).
metadata.labels
object
Arbitrary key/value pairs for grouping and selection.
spec
object
required
The desired state of the Project.

spec.services

The ordered list of containers to run. At least one service is required.
spec.services
ServiceDef[]
required
One or more service definitions.
spec.services[].name
string
required
Name of the service within the Project. This name is used as the DNS hostname inside the shared bridge network — other services can reach this container at http://{name}:{port}.
spec.services[].image
string
required
Docker image reference, for example nginx:alpine or postgres:15.
spec.services[].env
EnvVar[]
Environment variables injected into the container at runtime.
spec.services[].env[].name
string
required
Environment variable name, for example DATABASE_URL.
spec.services[].env[].value
string
Environment variable value.
spec.services[].volumeMounts
VolumeMount[]
Volumes to attach to this container. Each entry must reference a volume defined in spec.volumes.
spec.services[].volumeMounts[].name
string
required
Name of the volume to mount. Must match a name in spec.volumes.
spec.services[].volumeMounts[].mountPath
string
required
Absolute path inside the container where the volume is mounted, for example /var/lib/mysql.

spec.volumes

spec.volumes
VolumeDef[]
Named volumes shared across services. Optional.
spec.volumes[].name
string
required
Volume name. Referenced by spec.services[].volumeMounts[].name.
spec.volumes[].type
string
default:"Ephemeral"
required
Volume lifecycle type. Currently the only supported value is Ephemeral: the volume is discarded when the Project is stopped or moved to another node. No backup or restore occurs.

spec.ingress

spec.ingress
IngressDef[]
HTTP routing rules for exposing services. Optional.
spec.ingress[].name
string
required
Identifier for this ingress rule.
spec.ingress[].host
string
Hostname for the ingress rule. If the value contains a dot it is used verbatim; otherwise the final hostname is assembled as {host}.{environment}.{baseDomain}.
spec.ingress[].target
object
required
The backend service and port to forward traffic to.
spec.ingress[].target.service
string
required
Name of the service (must match a name in spec.services) to route traffic to.
spec.ingress[].target.port
integer
required
Port on the target service to forward traffic to.
spec.ingress[].access
object
Visibility settings for the ingress endpoint.
spec.ingress[].access.scope
string
default:"Internal"
required
Controls where the route is reachable. Currently the only supported value is Internal, which limits access to the Headscale overlay network.

spec.expireAt

spec.expireAt
string
An RFC 3339 timestamp. When set, the garbage collection controller automatically deletes the Project after this time. Useful for ephemeral preview environments. Example: 2026-04-30T00:00:00Z.

Examples

A single-service Project running an nginx web server. No volumes or ingress required.
nginx-project.yaml
apiVersion: caravanserai/v1
kind: Project
metadata:
  name: nginx-demo
spec:
  services:
    - name: web
      image: nginx:alpine

Validation

Caravanserai validates your manifest when you run caractrl apply. The following fields are required:
  • apiVersion
  • kind
  • metadata.name
  • spec.services (at least one entry)
  • spec.services[].name and spec.services[].image on each service
  • spec.volumes[].name and spec.volumes[].type on each volume
  • spec.ingress[].name, spec.ingress[].target.service, and spec.ingress[].target.port on each ingress rule
If you define a volumeMount in a service, the referenced volume name must exist in spec.volumes. The API rejects manifests with dangling volume references.
Service names resolve as hostnames within the Project’s Docker bridge network. Use the service name directly as the hostname when configuring inter-service connections — for example, WORDPRESS_DB_HOST: db connects the app service to the db service.