geerlingguy/mac-dev-playbook: A Mac Setup Playbook for Ansible Users
Mac setup and configuration via Ansible.
At a glance
- What is it?
- Jeff Geerling's Ansible playbook installs Homebrew packages, Mac App Store apps and dotfiles on a fresh macOS machine. It is opinionated by design, which is both the reason to use it and the reason to fork it.
- Who is it for?
- Adopt it if you already run Ansible and want a documented, tagged macOS provisioning flow you can edit: clone the repository, run ansible-galaxy install -r requirements.yml, then ansible-playbook main.yml --ask-become-pass. Do not adopt it if you want a one-line installer, if you cannot accept that Mac App Store apps need your Apple ID signed in, or if you need a rollback path, which the README does not document.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 56 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What mac-dev-playbook actually provisions on a new Mac
This is a personal provisioning playbook that its author made public. The README describes it plainly: it "installs and configures most of the software I use on my Mac for web and software development." That sentence is the whole scope. It is not a general purpose macOS configuration framework, and it does not try to be one.
The audience follows from that. You need to be comfortable reading YAML, running Ansible from a terminal, and editing a config file before the first run. In exchange you get a repeatable description of a machine: Homebrew casks, Homebrew formulae, Mac App Store applications, Composer, gem, npm and pip packages, dotfiles, and a set of macOS preference tweaks collected in a `.osx` dotfile. The README also notes that some parts of macOS resist automation, so a few manual steps remain, and says those are documented in the repository.
If your current setup process is a browser tab full of installers and a note that says "also install Xcode command line tools first", this replaces that with a file you can read.
How the playbook is structured: roles, config.yml and tags
The repository layout tells you most of the mechanism. `main.yml` is the entry point. `requirements.yml` pulls in external Ansible roles with `ansible-galaxy`. `default.config.yml` holds the defaults, and `config.yml` is where you override them. There is an `inventory` file, a `tasks/` directory, `templates/`, and a `tests/` directory backing the CI workflow in `.github/`.
The override model is the part worth understanding before you run anything. Any variable defined in `default.config.yml` can be replaced by creating `config.yml` in the same directory. The README gives a worked example covering `homebrew_installed_packages`, `mas_installed_apps`, `composer_packages`, `gem_packages`, `npm_packages`, `pip_packages`, and Dock configuration through `configure_dock`, `dockitems_remove` and `dockitems_persist`. It also states that any variable can be overridden this way and points to the supporting roles' documentation for the full list. That last pointer matters: the playbook itself does not enumerate every knob, so you will end up reading role documentation when you go beyond the example.
Tag filtering is the other mechanism. The README lists `dotfiles`, `homebrew`, `mas`, `extra-packages` and `osx` as the available tags, which lets you re-run one slice of provisioning instead of the whole machine. On a machine that already has most software installed, that is the difference between a two-minute run and a long one.
Install and first run on a clean Mac
The README's installation section starts with Apple's command line tools. Run `xcode-select --install` and complete the installer. Ansible comes next, and the README's steps assume pip3 rather than Homebrew, which is worth noting because it means Ansible is installed before Homebrew exists on the machine.
The README gives this sequence for Python and Ansible:
export PATH="$HOME/Library/Python/3.9/bin:/opt/homebrew/bin:$PATH"
sudo pip3 install --upgrade pip
pip3 install ansibleAfter that, clone the repository, then install the roles it depends on:
ansible-galaxy install -r requirements.ymlThen run the playbook itself. The README uses `--ask-become-pass`, so expect a prompt for your macOS account password, referred to as the BECOME password:
ansible-playbook main.yml --ask-become-passIf a Homebrew step fails, the README points at `brew doctor` as the diagnostic, and mentions agreeing to Xcode's license as a common cause.
To test a narrower change without reprovisioning everything, the README shows tag filtering:
ansible-playbook main.yml -K --tags "dotfiles,homebrew"The same playbook can drive a different Mac over SSH. The README says to enable Remote Login under System Settings > Sharing, or run `sudo systemsetup -setremotelogin on`, then edit the `inventory` file so the line starting with `127.0.0.1` becomes the target host and SSH user, with `--ask-pass` if you are not using keys.
The defaults are one person's workstation, not a neutral baseline
Read the application list in the README before you run this on a work machine. The default casks include Dropbox, Slack, Transmit, Handbrake, LICEcap, nvALT, Sequel Ace, ChromeDriver, Docker, Firefox, Google Chrome and Sublime Text. The default Homebrew formulae include nmap, iperf, wrk, httpie, php, go, node, nvm and gpg. Those are reasonable choices for a web developer who does performance testing and writes about tooling. They are not a neutral baseline, and installing them on a managed laptop may conflict with policy or with software your team already licenses.
The dotfiles step is the sharper edge. The README states that the author's dotfiles are installed into the current user's home directory, including the `.osx` dotfile, and that dotfiles management can be disabled with `configure_dotfiles: no` in your configuration. If you run the playbook with defaults on a machine that already has a tuned shell configuration, you are pointing a provisioning tool at your home directory. Set that variable deliberately rather than discovering it afterwards.
Mac App Store installs are the second constraint. The default `mas_installed_apps` entries are identified by numeric App Store IDs, and the README's example includes 1Password, Quick Resizer, Tweetbot and Xcode. App Store installation depends on being signed in with an Apple ID and on the apps still being available, which is why the README recommends a VM for testing only "some of the required testing" and warns that App Store apps and some proprietary software might not install properly there.
Where the playbook is the wrong tool
There is no rollback. The README documents installation, overrides, tags and remote use, and it does not document an uninstall or revert path. Ansible will happily remove a package if you change a state variable, but the playbook as published is a forward-only provisioning flow. If you need a machine you can return to a known prior state, this is not that.
It is also not a fleet management system. The README describes managing "another Mac on your network, or a hosted Mac", and the inventory file is a single line you edit. There is no discussion of many hosts, no inventory groups, no per-host variables. If you are provisioning a team of machines with different owners, you are using the wrong layer and should be looking at Ansible's inventory and group variable features directly, with this playbook as a reference rather than the product.
Finally, the maintenance model is personal. The repository is not archived and the last push was on 2026-08-04, so it is current. But it is one author's configuration, and the defaults reflect his choices. Upstream changes will arrive when he changes his own machine, not on a schedule you control.
Alternatives: a shell script, or your own role
The obvious alternative is a shell script. A `setup.sh` that calls `brew install` and `brew install --cask` a list of names is shorter, has no dependency on Ansible, and runs on a machine where Python packaging is broken. What it lacks is idempotence and structure. Ansible modules report whether they changed anything, so a second run is cheap and tells you what drifted. A shell script re-runs every command and gives you no state model. If your setup is fifteen Homebrew formulae, write the script. If it spans package managers, dotfiles and macOS defaults, the state model is the point.
The second alternative is writing your own Ansible role from scratch, which is what this playbook effectively is plus a set of supporting roles pulled in through `requirements.yml`. Starting from a blank role means you control every variable name and never inherit someone else's Dropbox cask. The cost is that you also write the macOS defaults handling, the Dock configuration and the App Store logic yourself. Reading `default.config.yml` and the `tasks/` directory here is a faster way to learn what that work involves than starting from an empty directory.
A related option for testing any of these approaches is running macOS in a VM. The README names UTM and Tart for that purpose, with the caveat about App Store and proprietary software.
Licence and upgrade cost
The repository's licence is reported as NOASSERTION, which means the GitHub licence classifier could not map the `LICENSE` file to a known identifier. The file exists at the repository root. Read it directly before you reuse the code in anything distributed; a classifier result of NOASSERTION tells you nothing about the terms, and this article is not legal advice.
Upgrade cost is the more practical question. Because overrides live in `config.yml` and the defaults live in `default.config.yml`, pulling upstream changes means reconciling two files: the defaults may have gained casks or packages you do not want, and your overrides may reference variables that changed. The README does not describe a migration process for that. Tag filtering limits the blast radius of a re-run, so a reasonable pattern is to pull, diff `default.config.yml` against what you last ran, and then execute only the tags affected. The playbook also depends on external roles from `requirements.yml`, so an upgrade can change behaviour without any change in this repository at all.
Editorial conclusion
Adopt it if you already run Ansible and want a documented, tagged macOS provisioning flow you can edit: clone the repository, run ansible-galaxy install -r requirements.yml, then ansible-playbook main.yml --ask-become-pass. Do not adopt it if you want a one-line installer, if you cannot accept that Mac App Store apps need your Apple ID signed in, or if you need a rollback path, which the README does not document. Before committing, read default.config.yml and decide which of the listed casks and packages you actually want, because the defaults are one person's workstation.
Frequently asked questions
What is an Ansible playbook used for?
An Ansible playbook is a YAML file that describes tasks to run on a machine, and in this project it is the entry point that installs and configures software on macOS. The README's example runs main.yml with ansible-playbook and a become password prompt.
Do I need Homebrew before running geerlingguy/mac-dev-playbook?
No. The README's installation order installs Ansible with pip3 first, and Homebrew appears in the default cask list, so it is installed by the playbook rather than being a prerequisite. Apple's command line tools are the stated prerequisite.
Can geerlingguy/mac-dev-playbook set up a Mac other than the one it runs on?
Yes. The README documents managing a remote Mac over SSH by enabling Remote Login, either in System Settings > Sharing or with sudo systemsetup -setremotelogin on, then editing the inventory file to point at that host and user.
How do I stop geerlingguy/mac-dev-playbook from overwriting my dotfiles?
Set configure_dotfiles: no in your configuration. The README states that the author's dotfiles are installed into the current user's home directory by default and that this setting disables that step.
Does geerlingguy/mac-dev-playbook install Mac App Store apps?
Yes, through the mas tag and the mas_installed_apps variable, where each app is listed with a numeric App Store id and a name. The README warns that App Store apps might not install properly inside a VM.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/geerlingguy-mac-dev-playbook)