Complete example
This example shows every common capability. Start from the smallest file you need and add fields only when repository evidence supports them.
version: 1
compose:
file: docker-compose.yml
previews:
- service: web
port: 5173
hmr: true
default_preview: web
inputs:
api_url:
required: true
description: URL exposed by the API service
outputs:
app_url:
from: service.url
environment:
VITE_API_URL:
from: input.api_url
APP_KEY:
secret: app-key
VITE_APP_ENV:
value: development
setup:
install:
- npm install
tests:
unit:
- npm test
cli:
seed:
command: npm run seed
description: Seed development dataTop-level fields
versionrequiredInteger 1.composerequiredCompose file and optional browser previews.default_previewoptionalPrimary preview service when more than one is declared.serviceoptionalService metadata for manifests that explicitly own their identity.inputsoptionalValues this repository needs from another selected service or project binding.outputsoptionalNamed values this repository makes available to other services.environmentoptionalLiteral values, runtime references, or secret names mapped to environment variables.setupoptionalNamed groups of evidence-backed setup commands.testsoptionalNamed groups of evidence-backed test commands.clioptionalRepository-specific cloud CLI entrypoints used by spin cli and spin x.Service metadata
Most connected services omit service because Spinnery already owns their name and type. When present, it is a mapping that requires non-empty name and type strings. storage_mode is optional and accepts shared_fs or local_mirror. Additional scalar metadata is preserved.
compose and previews
compose is required. compose.file is an optional non-empty repository-relative path and defaults to docker-compose.yml. Each preview accepts:
service— a unique, non-empty Compose service name.port— a container port from 1 through 65535.hmr— an optional YAML boolean; defaults tofalse.
default_preview is optional; when present, it must name a declared preview. Spinnery infers the default when exactly one preview exists. With multiple previews, declare it so the bare preview URL and service.url point to the intended primary app.
Inputs and outputs
Inputs declare what this repository needs. Outputs publish values produced by this repository. Matching names provide the automatic path between selected workspace services.
version: 1
compose:
previews:
- service: api
port: 8080
default_preview: api
outputs:
api_url:
from: service.urlversion: 1
compose:
previews:
- service: web
port: 5173
hmr: true
default_preview: web
inputs:
api_url:
required: true
environment:
VITE_API_URL:
from: input.api_urlAn input may be true for required, false or null for optional, or a mapping with required, description, and default_value. The older default spelling remains a compatibility alias; prefer default_value. An output is a reference string or a mapping with from and an optional description.
Runtime references
Reference strings resolve from the current workspace service:
input.<name>reads a declared input.service.name,service.slug,service.url, andservice.internal_urldescribe the current service.service.port.httpandservice.port.hmrexpose its assigned runtime ports.workspace.id,workspace.name,workspace.slug, andworkspace.url_basedescribe the current workspace.resource.<type>.<output>reads an available project resource output.
Cross-repository references such as services.api.url or service.api.url are not supported. Declare an input and connect it to a same-name output or an explicit project binding.
Environment and secrets
An environment entry may be a scalar or null literal shorthand. The mapping form must define exactly one source:
valuefor a scalar literal ornull.fromfor a runtime reference such asinput.api_url,service.url, or an availableresource.*value.secretfor the name of a Spinnery-managed secret. Secrets are required by default; addrequired: falseonly for an integration the service can run without.
Configure documented development defaults explicitly. If a checked-in local mode disables a production integration, use that safe mode and omit its unused credentials. Do not invent a bypass or infer that a variable is required merely because source code or an example template mentions it.
Reference the secret by name. Do not copy credentials from untracked .env files, private keys, or local credential files.
Setup, tests, and CLI commands
setup and tests map group names to one command or a list of commands. cli entries accept a command string or a mapping with command and an optional description. Select an entry with spin cli, then invoke it against the active workspace service with spin x. Add commands only when checked-in repository files prove them.
Rejected fields and references
Top-level ports, start, health, and resources are retired. Keep ports, commands, and health checks in Docker Compose. Configure managed resources and their bindings at the project level, then consume their available outputs through resource.*. A depends_on key is rejected anywhere, as are direct cross-repository service references.
Validation
spin validate
spin validate ./path/to/repository
spin validate ./spinnery.yml --jsonThe validator requires Docker Compose v2 and runs docker compose config --services with a 30-second limit. It exits nonzero for malformed manifests, missing Compose files, unknown preview services, and undeclared input.* references.