When Groceryx was merely an app idea I knew that I am going to need a server backend for it. Although it was a one man project, I've decided not to cut corners and invest some time in automating server provisioning and app deployment. Just to understand what are the benefits.
First I needed to choose what tool I'll use for that. The industry standard Chef and Puppet I found them quite an overkill. Their usual configuration has a dedicated server for just running Chef or Puppet. I wanted something simpler that I could run on my laptop. My primary requirement for this kind of tool was idempotency.
What's an idempotency? It's the ability to be sure of a server/service state regardless of the combination of commands run with overlapping interests. This way you describe the state for the server/service rather then commands to get to that state (like you do in a shell script). Consider this over simplified statement "Have this server an Apache service installed and be in a Started state with the following lines added to the configuration file." It doesn't matter how many times you run this statement you always end up with a running, properly configured Apache regardless of the initial server state (Apache is (or not) installed or configuration file is (or not) updated). This feature is the main reason for me not to use shell scripts (local or remote via Fabric, etc.). You can try to write them with idempotency in mind, but it's hard and one day you'll fail.
Ansible seemed like a perfect fit, so I went with it. No server side. Works over SSH. A YAML-based format for playbooks. At first, I wanted to use it only for server provisioning, setting up and configuring all needed services for a new server.
I have three type of servers: load balancing (Nginx) with an app servers (uwsgi) behind it talking to a DB server (PostgreSQL). All of them needed some common provisioning steps (like configuring system updates, time zone, SMTP server, my shell preferences, etc.). As well as some unique services configured for each of the servers. Ansible playbooks are designed to be modular. It's easy to keep each service configured in the separate playbooks, as well as to have common services setup described in one place for all servers.
Here is an example of my playbook for setting up Postfix on every server:
And here is another playbook for PostgreSQL setup for my DB server:
The name enclosed with double curly brackets is a variable with value defined in a separate "settings" file. Notice that there are no references to concrete hosts (IP addresses, host names). They are all configured separately allowing you to manage different environments with the same playbooks by having such "settings" file for each environment. I have Alpha environment with everything configured on one server hosted on Digital Ocean, Beta environment all-in-one server as well on Linode, and a Production environment with a separate load balancing, app, and DB servers. They all were provisioned from the same set of playbooks. The set of servers for each environment is defined separately with IP addresses, hostname, custom variables, etc.
Although playbooks are quite readable alone with your added comments it becomes your server documentation. It was the first benefit I did not expect to achieve. It's so great having this knowledge written somewhere and not only in my mind or in a bunch of shell scripts or in a notebook. Now you need to be sure that this server documentation does not become obsolete. The only thing you have to do to keep it up to date is to make any change to your server/service's configuration only through the playbooks. It sounds tedious, but it is not. Remember, that playbooks are idempotent, you can run them a million times and the server/service state will be as described. So if for example you need to change your PostgreSQL configuration, you make a change to the relevant playbooks and run the whole set of playbooks against the appropriate servers again. Most of the unchanged playbook statements will not generate any commands executed on a server since Ansible checks that the requirements for the given statements are already met.
Automated server provisioning saved me many times since then. The benefits of using Ansible for a one-man shop are not outright obvious. You have to spend more time on writing and tweaking playbooks at the beginning than if you set up all your servers manually one time. It's when you need to setup another set of servers, like another environment (testing, staging, etc.) or moving production servers to a different VPS provider. That's when it pays off. Not to mention using playbooks as a server documentation, describing all steps needed to configure a server (including the ones you long time forgot).