Deploy Scripts as Contracts
I used Ansible for my VPS. Playbooks, roles, the whole YAML ecosystem. It felt like the right choice—infrastructure as code, idempotent, declarative. But I found myself spending more time debugging Ansible than using the infrastructure it built.
The problem wasn’t Ansible. It was the abstraction layer. I have one VPS. I don’t need to scale to ten. I don’t have a team to onboard. The complexity of a configuration management system was solving problems I didn’t have, while creating problems I did.
The alternative
A deploy script. Bash, 80 lines. It:
- Builds the site locally (GitHub Actions, actually—separation of concerns)
rsyncs the artifacts to the VPS- SSHes in to restart nginx if the config changed
That’s it. No inventory files, no handlers, no Jinja2 templating. Just commands that do what they say.
The insight
Deploy scripts are contracts between you and future-you. They say: “To update this system, run this file.” The contract can be simple or complex, but it should be legible. When it breaks, you should be able to read it and know why.
Ansible playbooks are legible once you know Ansible. Bash is legible once you know Bash. I know Bash better. That’s the real calculation.
When I’ll switch back
If I get a second VPS. If I need to provision a fresh server more than twice a year. If the manual steps grow beyond what fits comfortably in a single readable script.
Until then, the 80-line contract holds.