Bootstraps a dev machine on Linux and macOS through an Ansible playbook, and on Windows natively through PowerShell, from one shared toggle file.
Setting up a fresh dev machine should not take a weekend of copy-pasting install commands. This repository treats workstation provisioning as infrastructure: declarative, idempotent, version-controlled. One command produces the same environment on Ubuntu, Fedora, Arch, CachyOS, and macOS through an Ansible playbook, and on Windows through a native PowerShell installer reading the same toggle files.
The trick is a small dispatch layer. group_vars/all.yaml declares intent
(install_chrome: true); per-OS dictionaries under vars/ map that intent to the correct
package manager.
- Idempotent. Run it ten times, get the same machine.
- One toggle file in group_vars/all.yaml drives the installation across every supported OS.
- Per-OS translation dictionaries under vars/ map a generic app name to
apt,dnf,pacman,brew,choco,winget,flatpak,snap, or AUR. - Profile overrides for live USB, minimal, or full installs without touching the base playbook.
- Native PowerShell entrypoint on Windows: installs the software catalogue and four system settings directly, with winget, Chocolatey, and npm, then installs Ansible inside WSL so it is there for later use.
- Docker Compose stack for local-dev databases and Keycloak.
- Home lab stack for an always-on Proxmox box: Home Assistant, private DNS, reverse proxy, dashboard, uptime monitor, file sync.
- OpenSpec workflow for spec-driven changes.
Ansible, Bash, PowerShell, Docker Compose, WSL, Keycloak, KDE Plasma, GNOME, systemd, OpenSpec.
flowchart TD
accTitle: Two execution paths sharing one toggle source
accDescr: setup.sh drives the Ansible playbook on the five Unix-like families. setup.ps1 drives native PowerShell installers on Windows and only touches WSL to install Ansible there for later use. Both read the same group_vars and vars/OS.yaml files.
A[setup.sh<br/>Ubuntu, Debian, Fedora, Arch, CachyOS, macOS] --> E[Ansible playbook<br/>site.yaml]
T[group_vars + vars/{OS}.yaml<br/>single source of truth for what to install] --> E
T --> W
E --> G[OS core roles, repo provisioning,<br/>SDK managers, software_installer<br/>apt, dnf, pacman, AUR, brew, flatpak, snap]
B[setup.ps1<br/>Windows] --> W[Native PowerShell installers<br/>WindowsSoftware, WindowsCustomInstalls,<br/>WindowsNpmTools, WindowsSettings]
W --> WSL[Installs Ansible inside WSL<br/>for later manual use, not driven from here]
On the five Unix-like families the phase order matches setup/ansible/site.yaml. OS core bootstrap first, then APT and DNF repo provisioning (keyrings, .list and .repo files for Chrome, Brave, VS Code, Sublime, kubectl, Docker), then SDK and runtime managers, then snapd and flatpak setup, then a single batched install per package manager, then custom installs for AppImages and macOS .dmg/.pkg artefacts.
On Windows, setup/setup.ps1 never drives the Ansible playbook: every task in the playbook gated on os_family == 'Windows' is unreachable, because the playbook only ever runs against WSL's own Linux environment. The software catalogue, the npm-based CLI tools, and the three system settings (optional features, WSL registration, the wallpaper) all install through setup/windows/ instead, reading the same group_vars and setup/ansible/vars/Windows.yaml the playbook would have used. The only thing this wizard still does with WSL is install Ansible inside the distribution, because that tool is wanted there. See docs/regression_ledger.md for how this was found and fixed.
| Category | Examples |
|---|---|
| Language toolchains | Python (Pyenv), Node.js (NVM), Java (SDKMAN), Dart and Flutter (FVM), Android SDK, Go, Rust |
| IDEs and editors | IntelliJ IDEA, VS Code, Android Studio, Cursor, Zed |
| CLI tools | git, zsh, oh-my-zsh, Docker, kubectl, k3d, Helm, Terraform, Ansible, gh, jq, fzf, ripgrep, uv, pipx, Go |
| AI tools | Claude Code, Claude Desktop, OpenCode, OpenCode desktop, OpenSpec |
| Apps | Chrome, Firefox, Slack, Discord, Spotify, OBS, VLC, Postman |
| Fonts | Nerd Fonts (FiraCode, JetBrainsMono, Hack, Meslo) |
| Desktop | KDE Plasma and GNOME setup, dotfiles, shell config |
The full per-OS list is in setup/software.md. The authoritative per-OS package mappings live under setup/ansible/vars/.
The interactive wizard presents a filter-as-you-type checklist for what to install. On Linux and macOS it installs Ansible and the required Galaxy collections first, then drives the playbook with your selections. On Windows it installs everything natively instead, through winget, Chocolatey, and npm, see the Architecture section above. The TUI uses gum on Linux and macOS and Microsoft.PowerShell.ConsoleGuiTools on Windows, both auto-installed on first run.
Linux / macOS (run as your regular user, not root):
bash setup/setup.shWindows (PowerShell 7+, not as Administrator, installs software and settings natively, then installs Ansible inside WSL for later use):
pwsh setup/setup.ps1The script prompts for sudo or admin only when needed.
Both entrypoints accept CLI flags to skip the wizard. Useful for CI, Packer images, or live USB bootstrap.
Show usage (Linux / macOS):
bash setup/setup.sh --helpApply a preset profile and skip the wizard (Linux / macOS):
bash setup/setup.sh --profile linux_live --non-interactivePowerShell equivalent on Windows:
pwsh setup/setup.ps1 -Profile linux_live -NonInteractiveOn Windows, -Profile is accepted so this exact command keeps working, but it selects nothing: profiles are Ansible variable overrides for the Linux side, and setup.ps1 no longer runs the playbook. Only -NonInteractive has any effect there, skipping the picker in favour of group_vars defaults.
Available flags:
--profile <name>/-Profile <name>: on Linux and macOS, apply setup/ansible/profiles/.yaml and skip selection. On Windows it is accepted for compatibility only and selects nothing, see the note above.--non-interactive/-NonInteractive: use group_vars defaults, never prompt.--no-color/-NoColor: disable themed output (also honoured viaNO_COLOR=1).--help/Get-Help .\setup.ps1: show usage.
For manual instructions, logging notes, and per-OS caveats, see setup/README_SETUP.md.
Ubuntu, Debian, Fedora, Arch, CachyOS, Manjaro, macOS through Ansible, Windows natively through PowerShell (WSL is used only to host Ansible for later use, not to run the playbook against Windows).
InstallationHelper/
├── setup/
│ ├── setup.sh # Linux/macOS entrypoint
│ ├── setup.ps1 # Windows entrypoint (native installers, then Ansible inside WSL)
│ ├── software.md # Per-OS software catalog
│ └── ansible/
│ ├── site.yaml # Main playbook
│ ├── group_vars/ # Cross-platform + OS-specific toggles
│ ├── vars/ # OS translation dictionaries
│ ├── profiles/ # Override profiles (linux_live, etc.)
│ └── roles/ # OS core, software_installer, sdk_manager, ai_tools, ...
├── local-dev/ # Docker Compose stack (MySQL, PG, Mongo, Keycloak)
├── openspec/ # Spec-driven change proposals & specs
├── docs/ # Per-OS guides + agent tooling docs
└── utils/ # Helper scripts
The Compose file under local-dev/ runs MySQL, PostgreSQL, MongoDB, and Keycloak (HTTPS) without polluting the host.
docker-compose -f ./local-dev/local-dev-docker-compose.yaml up -dPorts, credentials, and TLS notes live in local-dev/README_LOCAL_DEV.md.
Separate from the local-dev stack, homelab/ holds a Compose file and setup notes for an always-on server:
a Proxmox host running two virtual machines. One is Home Assistant OS with a Zigbee dongle passed through to it. The
other is Ubuntu with the Compose stack, running AdGuard Home for network wide DNS, Nginx Proxy Manager for private
.internal names, plus Homepage, Uptime Kuma, Syncthing, Perlite for reading a synced notes vault in a browser, and
Telegraf recording proof of short internet provider outages.
cd /opt/docker-stack && docker compose up -dStart at homelab/README_HOMELAB.md. The documents are written as a template, so fill in your own addresses where you see a placeholder.
The repo ships agent-standard imports for Claude Code, Kilo Code, OpenCode, and Codex, plus OpenSpec for spec-driven changes. The setup, integration points, and per-agent quirks are in docs/AGENT_TOOLING.md.
To propose a change:
-
Fork the repo and create a feature branch.
-
For non-trivial changes, draft an OpenSpec proposal under openspec/changes/. The workflow is documented in docs/AGENT_TOOLING.md.
-
Run the tier 1 gate before opening a PR. It includes the playbook syntax check and runs inside the gate container, never on your machine, so the same command works from Linux, Git Bash and WSL with Docker running:
bash e2e/run.sh
-
Open a PR with a clear description of the change and the OS(es) it affects.
Bug reports and feature requests are tracked via GitHub Issues.
Released under the MIT License.
| Doc | What's in it |
|---|---|
| setup/README_SETUP.md | Manual setup walk-through, script flags, logging, troubleshooting |
| setup/configuration.md | Toggle reference per OS |
| setup/software.md | Full per-OS software catalogue with install methods |
| docs/tool_usage.md | How to use the installed tools that are not self-explanatory |
| setup/version_sources.md | Where to look up the latest version for every pinned package |
| setup/bootstrap_machine_prompt.md | Post-install prompt: an agent records this machine in MACHINE.md |
| setup/MACHINE.md.example | Template for that machine record |
| setup/ansible/tags.md | Ansible --tags taxonomy for surgical partial runs |
| local-dev/README_LOCAL_DEV.md | Docker Compose stack: ports, credentials, image tags |
| local-dev/auth/README.md | Local auth: hosts file entries, local certificate authority, Let's Encrypt |
| local-dev/auth/Keycloak/README.md | Keycloak container build, token curl, OS trust store import |
| local-dev/auth/Keycloak/config.md | Realm export, client setup, export-import flow |
| local-dev/postgresql/README.md | Standalone Postgres build and run |
| homelab/README_HOMELAB.md | Home lab stack: services, ports, architecture, troubleshooting |
| homelab/proxmox_host_install.md | Installing Proxmox on the mini PC, updating it, node shell |
| homelab/proxmox_docker_vm.md | Building the Ubuntu VM on Proxmox and installing Docker Engine |
| homelab/home_assistant_vm.md | Home Assistant OS VM via qm, and Zigbee USB passthrough |
| homelab/home_assistant_backup.md | Home Assistant's own backups, encryption key, restore paths |
| homelab/adguard_dns.md | AdGuard Home setup, internal name rewrites, upstream resolvers, rate limit, blocklists, router DNS |
| homelab/nginx_proxy_manager.md | Reverse proxy entries for each internal name, and verification |
| homelab/ascend_web_search.md | AscendWebSearch stack: containers, secrets, resource limits, ngrok exposure |
| homelab/syncthing.md | Pairing devices, folder paths, ownership, file versioning |
| homelab/perlite_notes.md | Publishing a synced notes vault as a website |
| homelab/homepage_dashboard.md | Dashboard tiles, allowed hosts, status checks that bypass DNS |
| homelab/uptime_kuma.md | Service monitors and network monitors, ping versus HTTP checks, DNS resolution monitoring |
| homelab/network_watch.md | Telegraf recording proof of short provider outages, and reading it back afterward |
| homelab/backup_restore.md | vzdump, qmrestore, and backing up the service data directories |
| e2e/README_E2E.md | End-to-end harness: the pre-commit gate, and running the playbook in containers |
| docs/regression_ledger.md | Every regression the playbook has suffered, grouped by failure mechanism, with a prevention checklist |
| docs/AGENT_TOOLING.md | Agent-standards, OpenSpec, per-agent integration notes |
| docs/GLOBAL_SETUP.md | Machine-wide agent setup in ~/.config/agents, shared by every project |
| docs/MCP_SETUP.md | MCP server setup (Context7 only in this project) |
| docs/AI_TOOLS_ADDING.md | How to add a new npm-based AI / CLI tool to the playbook |
| docs/linux/virt_manager_setup.md | virt-manager setup notes for Linux hosts |
| docs/macos/utm_setup.md | UTM (Apple Virtualization) notes for macOS |
| docs/windows/virtualbox_setup.md | VirtualBox notes for Windows hosts |
| utils/mouseMove/README.md | Tiny Windows script that nudges the mouse to keep sessions awake |