Sailwright is an opinionated cross-platform automation toolkit for setting up, testing, and maintaining developer environments on macOS, Linux, and Windows. It combines Ansible with host-specific VM and system tooling so teams can manage local machines, remote hosts, and disposable test systems through one repository.
It is especially useful when a team has a mixed device fleet and wants one repeatable way to:
- onboard developers faster
- keep machine setup consistent across operating systems
- test provisioning changes safely before rolling them out
- reduce one-off scripts, tribal knowledge, and repeated support work
Sailwright is not a replacement for classic MDM/UEM tooling. It complements those tools by handling the developer-tooling and workflow layer that often remains manual, team-specific, and hard to reproduce.
Unlike raw ansible-playbook runs, chezmoi, Nix/Devbox, or devcontainers,
Sailwright pairs Ansible with disposable, host-specific VM build/provision
tooling so the same playbooks can be tested against real macOS/Linux/Windows
targets before they ever touch a developer's machine.
"Transform your system into a dev powerhouse with a touch of automation magic."
Left: the real quick-start commands. Right: a real, unattended VM build
(Ubuntu autoinstall) β no clicks, fully automated. See demo/ and
make demo-build-video.
TAG="$(curl -fsSL https://api.github.com/repos/csautter/sailwright/releases/latest | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n1)"
curl -fLO "https://github.com/csautter/sailwright/releases/download/${TAG}/sailwright_${TAG#v}_linux_amd64.tar.gz" && tar -xzf sailwright_*_linux_amd64.tar.gz
./sailwright install && ./sailwright build listSee π Getting Started below for macOS/Windows steps, cloning the repo instead, and running your first full workflow.
Without a shared automation baseline, cross-platform teams usually run into the same problems:
- developers spend hours figuring out how a machine is supposed to look
- onboarding depends too much on senior team members
- setup differences become hard to debug and reproduce
- OS-specific scripts drift over time
Sailwright addresses that by keeping setup logic in versioned Ansible roles and playbooks, then using host-appropriate tooling to apply and test them.
The project currently supports these host-to-target workflows:
| Host OS | What you can automate today |
|---|---|
| macOS | Managed workflows for macOS (Tart), Ubuntu (UTM), and Windows 11 (UTM) |
| Windows | Managed workflows for Ubuntu (Hyper-V) and Windows 11 (Hyper-V, plus VirtualBox as unstable) |
| Linux | Managed workflows for Ubuntu and Windows 11 with native QEMU/KVM via libvirt/virt-manager, plus direct Ansible runs and Docker-based Linux testing |
This means Sailwright can cover every currently supported guest OS family on a macOS host, and every currently supported family except macOS on a Windows host or Linux host.
Use the built-in discovery commands to see the exact combinations available on your current machine:
sailwright build list
sailwright create list
sailwright start list
sailwright provision list
sailwright stop list
sailwright destroy listNote: macOS guests are only supported on macOS hosts due to Apple platform and licensing restrictions.
Sailwright follows a few simple ideas:
- Ansible roles and playbooks are the source of truth for machine setup
- roles stay cross-platform where possible, with OS-specific handling where needed
- runs are idempotent, so the same workflow can be applied repeatedly
- execution is pull-oriented, so machines can run their own automation locally
--checkmode matters, so changes can be previewed before they apply
That makes it practical both for daily developer use and for testing changes in VMs before applying them to real machines.
You can also use the built-in wrapper for host-local provisioning:
sailwright provision local --check
sailwright provision local --proto ssh --check
sailwright provision local --playbook ./playbooks/setup.yml --check
sailwright provision localUse this when you want the same command surface on your real workstation that
you use for managed test targets. A bare run uses the playbook from configfile
ansible-role-sources.yml when configured; otherwise it falls back to the
bundled role-source smoke/example playbook. For platform defaults, Windows
transport behavior, cleanup flags, and rollback steps, see
Local Provisioning and
Windows Ansible Access. For direct
ansible-playbook examples, use
Running Playbooks.
You can either:
- download a release binary for normal use
- clone the repository when you want to edit playbooks, roles, or project code
Release assets are published on the
GitHub Releases page as
sailwright_<version>_<os>_<arch>.
After extraction, the executable is named sailwright on macOS/Linux and
sailwright.exe on Windows.
If you prefer the shorter sail command, create it yourself as a shell alias
such as alias sail=sailwright, or install a sail symlink that points to the
sailwright executable.
macOS / Linux example:
TAG="$(curl -fsSL https://api.github.com/repos/csautter/sailwright/releases/latest | sed -n 's/.*"tag_name": *"\([^"]*\)".*/\1/p' | head -n1)"
VERSION="${TAG#v}"
curl -fLO "https://github.com/csautter/sailwright/releases/download/${TAG}/sailwright_${VERSION}_linux_amd64.tar.gz"
tar -xzf "sailwright_${VERSION}_linux_amd64.tar.gz"
chmod +x ./sailwright
./sailwright build listWindows example:
$Release = Invoke-RestMethod "https://api.github.com/repos/csautter/sailwright/releases/latest"
$Tag = $Release.tag_name
$Version = $Tag.TrimStart("v")
Invoke-WebRequest -OutFile "sailwright_${Version}_windows_amd64.zip" "https://github.com/csautter/sailwright/releases/download/$Tag/sailwright_${Version}_windows_amd64.zip"
Expand-Archive "sailwright_${Version}_windows_amd64.zip" -DestinationPath .
.\sailwright.exe build listWhen you run a release binary outside a Git checkout, Sailwright extracts its embedded runtime assets into a managed app-data directory. See Managed Application Data for the default locations and override options.
git clone https://github.com/csautter/sailwright.git
cd sailwrightsailwright installThis runs scripts/macos/sailwright-install-dependencies.sh.
sailwright installThis runs scripts/linux/sailwright-install-dependencies.sh.
On macOS, Linux, and Windows, sailwright install --with-go also bootstraps the
Go toolchain.
Run the command from your normal shell. Windows will prompt for elevation through UAC if needed:
sailwright.exe install
# optionally with Go and VirtualBox support:
sailwright.exe install --with-go --virtualboxThis runs scripts/windows/sailwright-self-setup.ps1.
Start with the list commands before running a longer workflow:
sailwright build list
sailwright create list
sailwright provision listUse --help when you want the supported flags for a command:
sailwright --help
sailwright build --help
sailwright provision --helpThere are two common entry paths.
Use the built-in wrapper first when you want the shared command surface:
sailwright provision local --check
sailwright provision local --proto ssh --check
sailwright provision local --playbook ./playbooks/setup.yml --check
sailwright provision localConfigure ansible-role-sources.yml when you want the bare command to select
your machine setup automatically. Without that config, it runs the bundled
role-source smoke/example playbook. For more wrapper examples and
platform-specific local behavior, use Local Provisioning.
For direct localhost or remote-target ansible-playbook examples, use
Running Playbooks.
Example on a supported host:
sailwright build ubuntu --type server --arch amd64
sailwright create ubuntu --type server --arch amd64
sailwright provision ubuntu --type server --arch amd64 --check
sailwright provision ubuntu --type server --arch amd64If you are targeting Windows and remote access is not configured yet, start with Windows Ansible Access.
When you want to share reusable build artifacts through an OCI registry instead
of rebuilding them locally, use sailwright push and sailwright pull; see the
OCI build artifact registry workflow.
Sailwright collects no telemetry and phones home to nothing: it makes no network calls except the ones you explicitly ask for (release/self-update downloads, OCI registry push/pull, and the Ansible/VM/network operations your playbooks define).
Elevation is scoped and temporary rather than "run everything as root/admin."
On Windows, sailwright provision local creates a dedicated local admin
account with a random or rotated password, enables WinRM-over-HTTPS or SSH
only on the loopback address for the duration of the run, and restores the
prior WinRM/SSH/firewall state during cleanup β see
Windows Ansible Access for
the exact mechanism. On macOS/Linux, install and provisioning scripts prompt
for sudo only for the specific steps that need it.
The root README is the fast entry point. Use these guides when you want the next level of detail:
- Local Provisioning for the
sailwright provision localwrapper, platform defaults, and Windows cleanup flags - Running Playbooks for direct localhost,
remote-host, VM, and Windows
ansible-playbookexamples - Testing Workflows for OCI artifact registry push/pull and host-specific VM and Docker test flows
- Automated Quick-Start for the recorded, single-command end-to-end run of the full VM lifecycle
- Demo Recordings for how the quick-start recordings and website terminal demo are produced
- Managed Application Data for cache, runtime, and app-data locations
- Ansible Role Sources for layering local and Git-backed role roots during provisioning
- Windows Ansible Access for manual WinRM and SSH setup on Windows targets plus OpenSSH rollback notes
- Example Ansible Roles for the current sample role catalog and repository layout
- Troubleshooting Guide for rare host-specific issues
The repository already includes example roles for common developer tooling such
as brew, java, jetbrains, k9s, kind, kubectl, kubelogin,
openssh, python, and spotify.
Use Example Ansible Roles as the catalog and
roles/ as the source of truth.
Contributions are welcome. Good areas to improve include:
- more roles and playbooks
- broader cross-platform coverage
- better docs and troubleshooting guidance
- bug fixes and test coverage
See CONTRIBUTING.md for contribution terms and the Contributor License Agreement (CLA).
Sailwright uses a dual-licensing model.
Copyright (c) 2026 Carl-Christian Sautter. Open source use is licensed under AGPLv3, and commercial licensing is offered separately.
The community edition is licensed under the GNU Affero General Public License v3 (AGPLv3).
See LICENSE.md.
If you need to use Sailwright in commercial products, SaaS platforms, or other closed-source environments without AGPL obligations, a separate commercial license is available.
For commercial inquiries, visit www.c-sautter.de for contact details.
Early releases were published under the MIT License. If you use an older tag or release, refer to the license file included with that version.
This project grew out of real onboarding and support pain in mixed-OS teams. The goal is simple: make machine setup reproducible, testable, and much less dependent on memory or hand-written host-specific scripts.

