Project Configuration (spuff.yaml)
spuff supports per-project configuration via a spuff.yaml file in your project root. This enables environment as code - defining your development environment declaratively alongside your source code.
See also: Spuff Specification for the formal specification with validation rules and conformance requirements.
Overview
When you run spuff up in a directory containing a spuff.yaml file, spuff will:
Read the project configuration
Apply any resource overrides (size, region)
Provision the VM with the project config embedded
Install bundles, packages, and services automatically
Clone repositories and run setup scripts
File Location
Place spuff.yaml in your project root (same directory as your git repository). spuff will search up the directory tree to find it.
my-project/
├── spuff.yaml # Project configuration
├── spuff.secrets.yaml # Secrets (add to .gitignore!)
├── docker-compose.yaml # Services (optional)
└── src/Complete Example
Configuration Reference
version
versionType: string Default: "1"
Spec version for future compatibility.
name
nameType: string (optional) Default: Directory name
Custom name for the environment.
resources
resourcesOverride global VM configuration. CLI flags take precedence over project config.
Precedence: CLI flags > spuff.yaml > ~/.spuff/config.yaml
bundles
bundlesPre-configured language toolchains. Each bundle installs the compiler/runtime plus essential development tools (LSPs, linters, formatters).
Available bundles:
rust
rustup, cargo, rust-analyzer, clippy, rustfmt, mold
go
go, gopls, delve, golangci-lint, air
python
python3.12, pip, venv, uv, ruff, pyright, ipython
node
node 22 LTS, npm, pnpm, typescript, eslint, prettier
elixir
erlang/OTP, elixir, mix, elixir-ls, phoenix
java
openjdk 21, maven, gradle, jdtls
zig
zig, zls
cpp
gcc, clang, cmake, ninja, clangd, gdb, lldb
ruby
ruby, bundler, solargraph, rubocop
packages
packagesAdditional system packages to install via apt.
services
servicesDocker services configuration. Uses your project's docker-compose.yaml.
Note: spuff doesn't duplicate docker-compose configuration - it uses your existing compose file.
repositories
repositoriesClone additional repositories into the environment.
SSH Agent Forwarding: spuff uses SSH agent forwarding, so your local SSH keys work for cloning private repos.
env
envEnvironment variables set on the VM.
Variable resolution: References to $VAR, ${VAR}, or ${VAR:-default} are resolved from your local environment before being sent to the VM.
setup
setupShell commands executed after bundles and packages are installed.
Scripts are executed in order. If any script fails, subsequent scripts are skipped.
ports
portsPorts for automatic SSH tunneling. When you run spuff ssh, these ports are forwarded from your local machine to the VM.
This allows you to work "locally" (browser, IDE) while connected to the remote VM.
ai_tools
ai_toolsType: string or list (optional) Default: all
Controls which AI coding tools are installed on the VM.
Available tools:
claude-code
@anthropic-ai/claude-code
Anthropic's Claude Code CLI
codex
@openai/codex
OpenAI Codex CLI
opencode
opencode-ai
Open-source AI coding assistant
copilot
@github/copilot
GitHub Copilot CLI
CLI override: spuff up --ai-tools claude-code,copilot
Precedence: CLI --ai-tools > spuff.yaml > ~/.spuff/config.yaml > default (all)
See AI Tools documentation for authentication and configuration details.
volumes
volumesMount remote VM directories locally via SSHFS for bidirectional file editing.
Fields:
source
Yes
Local directory path (relative to spuff.yaml or absolute)
target
Yes
Remote directory path on the VM
mount_point
No
Where to mount remote directory locally
Mount Point Resolution:
If
mount_pointis specified, use itIf only
sourceis specified, mount oversourcefor bidirectional editingOtherwise, auto-generate under
~/.local/share/spuff/mounts/<instance>/<path>
Behavior during spuff up:
Remote directory is created on the VM
Local
sourceis synced to remotetargetvia rsyncRemote
targetis mounted locally via SSHFS
Behavior during spuff down:
All mounted volumes are force-unmounted before VM destruction
This prevents SSHFS from hanging when the remote server disappears
Requirements:
macOS: macFUSE and
sshfs(brew install macfuse sshfs)Linux:
fuseandsshfspackages
CLI Commands:
hooks
hooksLifecycle scripts for custom automation.
Secrets Management
spuff.secrets.yaml
Store sensitive values in a separate file that's not committed to git:
Add to .gitignore:
Merge behavior: spuff.secrets.yaml is merged with spuff.yaml, with secrets taking precedence.
Environment Variable Resolution
Reference local environment variables in your config:
CLI Integration
spuff up
spuff upWhen spuff.yaml is present:
spuff status --detailed
spuff status --detailedShows project setup progress:
spuff logs
spuff logsView project setup logs:
spuff ssh
spuff sshConnects with automatic port tunneling:
Logging
All project setup activities are logged to /var/log/spuff/ on the VM:
Agent API Endpoints
The spuff-agent exposes these endpoints for project management:
/project/config
GET
Current project configuration
/project/status
GET
Detailed setup progress
/project/setup
POST
Start project setup
These are used internally by the CLI but can be accessed directly via SSH tunnel.
Best Practices
Keep spuff.yaml in version control - The environment becomes reproducible
Use spuff.secrets.yaml for secrets - Never commit credentials
Prefer bundles over individual packages - They include LSPs and dev tools
Use
docker-compose.yamlfor services - Don't duplicate configTest your setup scripts locally first - Saves provisioning time
Use specific versions in setup scripts - Avoid "works on my machine" issues
Troubleshooting
Project config not detected
Solution: Ensure spuff.yaml is in your project root and you're running spuff up from within the project directory.
Bundle installation failed
Check the bundle-specific log for errors. Common issues:
Network connectivity
Disk space
Package conflicts
Services not starting
Ensure your docker-compose.yaml is valid and doesn't require manual configuration.
Setup script failed
View the specific script's output. The script runs in the user's home directory by default.
Last updated
Was this helpful?