{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://serverkit.ai/serverkit-yaml.schema.json", "title": "ServerKit Manifest", "description": "Declarative deployment manifest (serverkit.yaml / serverkit.json). A version:1 file describes a whole deployment — services, databases, disks, backups, env vars, cron and domains — that ServerKit parses, diffs against live state, and applies. camelCase is canonical; snake_case aliases are accepted everywhere.", "type": "object", "required": ["version"], "additionalProperties": true, "properties": { "version": { "description": "Manifest schema version. 1 selects the declarative multi-service spec. Files without version:1 are legacy flat manifests and keep their v0 behavior.", "const": 1 }, "server": { "description": "Optional default fleet target (agent server name or id). A service may override it. Omit for the local panel host.", "type": "string", "minLength": 1 }, "project": { "description": "Optional project name this manifest maps to. Defaults to the repository name.", "type": "string", "minLength": 1 }, "envVars": { "description": "Manifest-level (project-scoped) environment variables, applied to every service as a shared variable group.", "type": "array", "items": { "$ref": "#/definitions/envVar" } }, "services": { "description": "The services that make up this deployment.", "type": "array", "minItems": 1, "items": { "$ref": "#/definitions/service" } }, "domains": { "description": "Public domains routed to services.", "type": "array", "items": { "$ref": "#/definitions/domain" } }, "capabilities": { "description": "Closed Recipe capability set. Every configure/verify step must declare its type:kind capability here so older panels can refuse unsupported Recipes before doing work.", "type": "array", "uniqueItems": true, "items": { "type": "string", "pattern": "^[a-z]+:[a-z0-9._-]+$" } }, "requires": { "description": "Recipe host requirements used by preflight and provider placement.", "$ref": "#/definitions/recipeRequirements" }, "configure": { "description": "Ordered, idempotent Recipe steps run after the declared services are applied.", "type": "array", "items": { "$ref": "#/definitions/recipeStep" } }, "verify": { "description": "Outcome checks. A successful process is not a successful Recipe until these checks pass.", "type": "array", "items": { "$ref": "#/definitions/recipeVerifyStep" } } }, "definitions": { "service": { "type": "object", "required": ["name", "type"], "additionalProperties": true, "properties": { "name": { "description": "Unique service name within the manifest. Becomes the Application (or ManagedDatabase) name.", "type": "string", "pattern": "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$", "minLength": 1, "maxLength": 63 }, "type": { "description": "Service kind. web/worker/static/docker create Applications; postgres/mysql/mariadb/redis create ManagedDatabases.", "enum": ["web", "worker", "static", "docker", "postgres", "mysql", "mariadb", "redis"] }, "runtime": { "description": "Build runtime hint for app services (e.g. python, node, static, docker). Ignored for db types.", "type": "string" }, "buildCommand": { "description": "Command used to build the app (buildpack override).", "type": "string" }, "startCommand": { "description": "Command used to start the app (buildpack override).", "type": "string" }, "dockerfilePath": { "description": "Build the service from this Dockerfile in the repository (repo root = build context) — the monorepo path: one repo, several services, each built from its own Dockerfile. Relative path only; mutually exclusive with `image` and `containers`.", "type": "string", "minLength": 1 }, "port": { "description": "Container port the app listens on.", "type": "integer", "minimum": 1, "maximum": 65535 }, "ports": { "description": "Typed L4 port publishes (appliance tier). Each entry is a raw tcp/udp publish — the escape hatch for services that speak more than HTTP (media/UDP, VoIP, brokers). The legacy scalar `port` keeps its HTTP/nginx behavior and is separate.", "type": "array", "items": { "$ref": "#/definitions/portDecl" } }, "image": { "description": "Container image reference for a BYO-image app (appliance tier) — a first-class peer of the buildpack path.", "type": "string" }, "registry": { "description": "Named private registry to authenticate against before pulling `image`. A registry that is unknown or has no stored credential is a plan-time blocker.", "type": "string" }, "hostRequirements": { "description": "Elevated host requirements (appliance tier) — privileged/capAdd/sysctls/devices/kernelModules. Always listed in plain words in the plan; never applied silently.", "$ref": "#/definitions/hostRequirements" }, "bootstrap": { "description": "First-boot bootstrap (appliance tier). Runs once via `docker compose run --rm` after volumes exist and before the first up — for appliances that generate a config tree once.", "$ref": "#/definitions/bootstrap" }, "containers": { "description": "A multi-container UNIT (appliance tier): one service rendered as one compose project with a private network and health-gated dependsOn. Mutually exclusive with the buildpack keys. Keyed by container name.", "type": "object", "minProperties": 1, "additionalProperties": { "$ref": "#/definitions/container" } }, "healthCheckPath": { "description": "HTTP path used for health checks and the zero-downtime restart gate.", "type": "string" }, "autoDeploy": { "description": "When true, a push that changes this service's declared config auto-applies. Otherwise the manifest flips to pending and waits for an explicit apply.", "type": "boolean", "default": false }, "version": { "description": "Engine version for db services (e.g. \"16\" for postgres).", "type": ["string", "number"] }, "server": { "description": "Per-service fleet target override.", "type": "string" }, "cpu": { "description": "CPU limit (cores).", "type": ["number", "string"] }, "memory": { "description": "Memory limit (e.g. 512MB, 1GB).", "type": ["string", "number"] }, "envVars": { "description": "Per-service environment variables.", "type": "array", "items": { "$ref": "#/definitions/envVar" } }, "disks": { "description": "Persistent volumes for app services.", "type": "array", "items": { "$ref": "#/definitions/disk" } }, "disk": { "description": "Single primary disk for db services.", "$ref": "#/definitions/disk" }, "cron": { "description": "Scheduled job(s) run in the service context.", "oneOf": [ { "$ref": "#/definitions/cron" }, { "type": "array", "items": { "$ref": "#/definitions/cron" } } ] } } }, "bootstrap": { "type": "object", "required": ["command"], "additionalProperties": true, "properties": { "command": { "description": "Command run once inside a throwaway service container.", "type": "string", "minLength": 1 }, "timeoutSeconds": { "description": "Max seconds the bootstrap may run before it is killed and the step fails.", "type": "integer", "minimum": 1 } } }, "container": { "type": "object", "additionalProperties": true, "properties": { "image": { "description": "Container image reference.", "type": "string" }, "registry": { "description": "Named private registry to authenticate against before pulling.", "type": "string" }, "ports": { "type": "array", "items": { "$ref": "#/definitions/portDecl" } }, "disks": { "type": "array", "items": { "$ref": "#/definitions/disk" } }, "envVars": { "type": "array", "items": { "$ref": "#/definitions/envVar" } }, "bootstrap": { "$ref": "#/definitions/bootstrap" }, "hostRequirements": { "$ref": "#/definitions/hostRequirements" }, "healthCheck": { "$ref": "#/definitions/healthCheck" }, "dependsOn": { "type": "array", "items": { "$ref": "#/definitions/dependsOn" } } } }, "healthCheck": { "type": "object", "additionalProperties": true, "properties": { "cmd": { "description": "Shell command probe (rendered as CMD-SHELL).", "type": "string" }, "httpPath": { "description": "HTTP path probed with wget on localhost.", "type": "string" }, "interval": { "description": "e.g. 30s.", "type": "string" }, "timeout": { "description": "e.g. 5s.", "type": "string" }, "retries": { "type": "integer", "minimum": 1 } } }, "dependsOn": { "type": "object", "required": ["service"], "additionalProperties": true, "properties": { "service": { "description": "Sibling container name in the same unit.", "type": "string" }, "condition": { "description": "healthy waits for the dependency's healthcheck; started only waits for it to start.", "enum": ["healthy", "started"] } } }, "hostRequirements": { "type": "object", "additionalProperties": true, "properties": { "privileged": { "description": "Run privileged (big hammer — always shown in the plan, never silent).", "type": "boolean" }, "capAdd": { "description": "Linux capabilities to add.", "type": "array", "items": { "type": "string" } }, "sysctls": { "description": "sysctl key/value pairs.", "type": "object" }, "devices": { "description": "Host devices to expose.", "type": "array", "items": { "type": "string" } }, "kernelModules": { "description": "Kernel modules the workload needs (advisory /proc/modules check only).", "type": "array", "items": { "type": "string" } } } }, "portDecl": { "type": "object", "required": ["port"], "additionalProperties": true, "properties": { "port": { "description": "Host port to publish.", "type": "integer", "minimum": 1, "maximum": 65535 }, "containerPort": { "description": "Container port to publish to. Defaults to the host port.", "type": "integer", "minimum": 1, "maximum": 65535 }, "protocol": { "description": "L4 protocol. Defaults to tcp.", "enum": ["tcp", "udp"] }, "expose": { "description": "public = bind 0.0.0.0 and open the firewall; local = bind 127.0.0.1 only. Defaults to public.", "enum": ["public", "local"] } } }, "envVar": { "type": "object", "required": ["key"], "additionalProperties": true, "properties": { "key": { "description": "Environment variable name.", "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }, "value": { "description": "Literal value. Use only for non-secrets — secrets never live in the manifest.", "type": ["string", "number", "boolean"] }, "fromSecret": { "description": "Reference to a vault secret by name; resolved at injection time, never stored in the env row.", "type": "string" }, "fromService": { "description": "Reference to a sibling service's connection property.", "type": "object", "required": ["name", "property"], "additionalProperties": false, "properties": { "name": { "type": "string" }, "property": { "type": "string", "enum": [ "connectionString", "host", "port", "database", "username", "password", "url" ] } } }, "fromServer": { "description": "Reference to the service's own target server identity (appliance tier). publicIp is what a NAT'd media/WebRTC bridge must advertise to clients.", "type": "object", "required": ["property"], "additionalProperties": false, "properties": { "property": { "type": "string", "enum": ["publicIp", "hostname"] } } }, "generate": { "description": "When true, ServerKit generates a random value on first apply, stores it as a vault secret, and rewires this entry as a fromSecret reference.", "type": "boolean" } } }, "disk": { "type": "object", "additionalProperties": true, "properties": { "name": { "description": "Volume name.", "type": "string" }, "mountPath": { "description": "Mount path inside the container.", "type": "string" }, "size": { "description": "Requested size (e.g. 10GB). Recorded; not enforced by the local driver.", "type": ["string", "number"] }, "backup": { "$ref": "#/definitions/backup" } } }, "backup": { "type": "object", "additionalProperties": true, "properties": { "schedule": { "description": "Backup frequency.", "type": "string", "enum": ["hourly", "daily", "weekly", "monthly"] }, "retain": { "description": "Number of backups to keep.", "type": "integer", "minimum": 1 } } }, "cron": { "type": "object", "required": ["schedule", "command"], "additionalProperties": true, "properties": { "name": { "type": "string" }, "schedule": { "description": "Cron expression (5-field) or shorthand (daily, hourly...).", "type": "string" }, "command": { "description": "Command to run.", "type": "string" } } }, "domain": { "type": "object", "required": ["host"], "additionalProperties": true, "properties": { "host": { "description": "Fully-qualified hostname (e.g. api.example.com).", "type": "string", "minLength": 1 }, "service": { "description": "Name of the service this domain routes to.", "type": "string" }, "ssl": { "description": "Certificate mode. auto = best-effort issuance (never blocks); off = HTTP only.", "oneOf": [ { "type": "string", "enum": ["auto", "off", "on"] }, { "type": "boolean" } ] } } }, "recipeRequirements": { "type": "object", "additionalProperties": true, "properties": { "cpuCores": { "type": "integer", "minimum": 1 }, "memoryMB": { "type": "integer", "minimum": 1 }, "diskGB": { "type": "integer", "minimum": 1 }, "igpu": { "enum": ["required", "preferred", "none"] }, "gpu": { "enum": ["required", "preferred", "none"] } } }, "recipeStep": { "type": "object", "required": ["id", "type", "kind"], "additionalProperties": true, "properties": { "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" }, "type": { "enum": ["probe", "derive", "apply", "handoff", "verify"] }, "kind": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" }, "title": { "type": "string", "minLength": 1 }, "description": { "type": "string" }, "dependsOn": { "type": "array", "uniqueItems": true, "items": { "type": "string" } }, "params": { "type": "object" }, "ttlSeconds": { "type": "integer", "minimum": 1 }, "input": { "$ref": "#/definitions/recipeInput" } } }, "recipeVerifyStep": { "type": "object", "required": ["id", "kind"], "additionalProperties": true, "properties": { "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" }, "type": { "const": "verify" }, "kind": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" }, "title": { "type": "string", "minLength": 1 }, "description": { "type": "string" }, "dependsOn": { "type": "array", "uniqueItems": true, "items": { "type": "string" } }, "params": { "type": "object" } } }, "recipeInput": { "type": "object", "required": ["key", "label"], "additionalProperties": true, "properties": { "key": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" }, "label": { "type": "string", "minLength": 1 }, "help": { "type": "string" }, "url": { "type": "string" }, "secret": { "type": "boolean" } } } } }