Skip to content

Configuration Reference

The slipp.yaml file configures a project for slipp operations.

  • Directoryyour-project/
    • slipp.yaml
    • Directoryinventory/
    • playbook.yml

Created automatically by slipp launch or slipp deploy --name.

slipp.yaml
# Project identity
name: myapp
# Ansible paths
inventory: inventory/hosts
playbook: playbook.yml
roles_path:
- roles
galaxy_path: roles/galaxy
# Secrets
vault: inventory/host_vars/myserver/vault.yml
# Runtime (auto-detected if omitted)
runtime: docker
# Routing: service -> domain/path (seeded by slipp launch, hand-editable)
expose:
frontend:
domain: app.example.com
backend:
domain: app.example.com
path: /api
# Managed roles (for filtering in slipp ps)
managed_roles:
- myapp
- myapp-worker
# Tag presets for deployments
tag_presets:
install: "--tags install-all"
setup: "--tags setup-all --skip-tags start"
restart: "--tags restart"
update: "--tags update-app"
# Run profiles for local development
runs:
dev:
cmd: npm run dev
tunnels:
out: [5173:app.example.com@myserver]
vaults: [myapp]
dev-staging:
extends: dev
env:
- VITE_ENV=staging

Required. Project identifier used in:

  • Global registry
  • Service filtering with slipp ps -p
  • Cross-project vault access
name: myapp

Path to Ansible inventory file, relative to project root.

inventory: inventory/hosts

Default: inventory.yml (or inventory-{env}.yml for non-production environments). Optional — projects that only use run profiles (no deploy target) don’t need an inventory.

Path to main Ansible playbook.

playbook: playbook.yml

Default: playbook.yml

List of directories to search for Ansible roles. Passed to Ansible via ANSIBLE_ROLES_PATH.

roles_path:
- roles
- ../shared-roles

Default: [] (Ansible default)

Install directory for external roles from requirements.yml. Automatically added to roles_path at runtime.

galaxy_path: roles/galaxy

Default: roles/galaxy (used automatically if not set — no need to configure this unless you want a different path).

slipp launch generates a requirements.yml listing the Ansible collections your project needs (e.g. community.docker or containers.podman, depending on your container runtime). When requirements.yml exists, slipp deploy installs both roles and collections from it automatically before running the playbook.

Path to Ansible vault file for secrets.

vault: inventory/host_vars/myserver/vault.yml

Used by:

  • slipp secrets commands
  • slipp run --vault for environment injection

How the app runs on the server: systemd, docker, or podman.

runtime: docker

docker and podman generate container-based deployments (Dockerfile, compose, container-specific Ansible collections). systemd generates a native app deployment (direct process management, no containers).

Auto-detected from playbook if omitted (container runtimes only — systemd must be set explicitly).

runtime: systemd works for both Node and Python projects. For a Python project it renders a distinct systemd role (a uv-managed venv, uv sync --frozen) rather than the Node role. Two slipp launch flags only apply to Python systemd deploys:

Terminal window
slipp launch --name myapp --python-extra prod --exec-args "--workers 4"
FlagPurpose
--python-extraOptional dependency group passed as uv sync --extra <group>
--exec-argsExtra arguments appended to the systemd unit’s ExecStart

Routes services to public domains/paths. Each key is a detected service name; the value is where it’s served.

expose:
frontend:
domain: app.example.com
# path defaults to "/"
backend:
domain: app.example.com
path: /api
worker:
domain: worker.app.example.com
host: secondary-host # deploy target other than the primary host
port: 8081 # override only to resolve a same-host port collision
internal: true # force wg-manage --internal-tls, no public domain/DNS needed
FieldDescription
domainRequired. FQDN serving this service
pathPath prefix on the domain. Default: /
hostInventory host name to deploy this service to. Default: the project’s primary host
portOverride the service’s container/host port — only needed to resolve a same-host port collision between two services
internalForce wg-manage --internal-tls for this service regardless of the project’s --public launch flag (--proxy wg-manage only)

Seeding. slipp launch seeds this block automatically if it doesn’t exist yet, following one convention: a single service gets the bare domain at /; a frontend+backend pair puts the frontend at / and the backend at /api on the same domain; every other service gets its own {name}.{domain} subdomain.

Editing. A hand-edited block always wins — slipp launch never overwrites an existing expose: block, only fills one in when there isn’t one yet. To change routing, edit the entries directly and redeploy. To remove a service from public exposure, delete its entry — that’s the supported opt-out, not an oversight.

Domain changes. If the project’s app_domain changes but the expose: block still points at the old domain, slipp launch only warns (expose: block doesn't reference <domain>) — it never rewrites your routing for you. Delete the block to have it re-seed against the new domain.

List of Ansible role names this project manages. Used to filter slipp ps output.

managed_roles:
- myapp
- myapp-worker
- myapp-scheduler

Services from these roles appear in slipp ps without --all.

Named shortcuts for Ansible tag combinations.

tag_presets:
install: "--tags install-all"
setup: "--tags setup-all --skip-tags start"
restart: "--tags restart"

Use with:

Terminal window
slipp deploy install
slipp deploy setup

Run profiles for local development. Each key is a profile name. See Local Development for concepts and slipp run for CLI usage.

runs:
dev:
cmd: npm run dev
vaults: [myapp]
env:
- NODE_ENV=development
tunnels:
out: [5173:app.example.com@myserver]
in: [postgres:5432@myserver]
auth: "user:<bcrypt-hash>"
proxy:
- from: app.example.com/api
to: localhost:8080/api
host: myserver
dev-staging:
extends: dev
env:
- VITE_ENV=staging

A profile can inherit from another with extends — the child’s fields replace the parent’s (no merging). Personal overrides go in the untracked .slipp/runs.local.yaml (same name shadows the tracked profile entirely).

The inventory file uses standard Ansible YAML format:

inventory/hosts
all:
hosts:
myserver:
ansible_host: 192.168.1.100
ansible_user: slipp
ansible_port: 22

An inventory can have more than one host for the same project and environment — e.g. a frontend on one VPS and a worker service on another. Exactly one host is the primary (is_primary: true, auto-managed — slipp launch fails loud if the inventory ever ends up with zero or more than one). It owns the project’s public identity (app_domain, DNS, primary Caddy/wg-manage routing); every other host is secondary.

The interactive slipp launch prompt only ever creates the primary host. Add secondary hosts with the CLI, not by hand-editing the inventory file:

Terminal window
slipp hosts add worker --host 203.0.113.9 --runtime docker
slipp hosts list
slipp hosts remove worker

Assigning a service to a secondary host is a separate step: set expose: <service>: host: <name> in slipp.yaml, then run slipp launch --reconfigure to regenerate the deployment. See expose: and slipp hosts.

IP-reuse rules. Adding a host whose IP is already used by another host within this same project’s inventory is a hard error — no legitimate reason for one project to deploy two names to the same box. An IP already used by a different registered project is allowed (that’s real, deliberate host sharing, e.g. two projects on one VPS) — slipp hosts add/slipp projects add print an informational notice naming the other project(s), but never block it. On a shared host, unqualified slipp logs/exec/ssh/status <service> commands become ambiguous and must be qualified as project:service — see Service lookup grammar.

Per-host configuration in host_vars/:

  • Directoryinventory/
    • hosts
    • Directoryhost_vars/
      • Directorymyserver/
        • vars.yml
        • vault.yml
inventory/host_vars/myserver/vars.yml
domain: app.example.com
app_port: 3000
database_host: localhost
database_password: "{{ vault_database_password }}"

Projects are tracked globally at ~/.config/slipp/projects.json:

{
"projects": [
{
"name": "myapp",
"project_path": "/home/user/projects/myapp",
"registered_at": "2024-01-15T10:30:00Z"
}
]
}

View with:

Terminal window
slipp projects list

For multiple environments, use separate inventory files:

  • Directoryinventory/
    • production
    • staging
    • development

Deploy to specific environment:

Terminal window
slipp deploy staging

Or configure in slipp.yaml per environment (not yet supported - use CLI flags).

If upgrading from an older slipp version:

  1. Rename ansictl.yaml to slipp.yaml
  2. The format is identical - no content changes needed