I've been using Ansible to deploy ~10 services from git repos to a set of KVMs. After paying the time tax of writing the playbooks, I just update a release version variable for the service I want to update and rerun the playbook. It will even take care of the systemd restarts.
I'm pretty happy with that set up. Ansible has just enough smarts to be less tedious than shell scripting, but dumb enough that it's behavior is easy to figure out in most cases.
Now I'm tempted to try running it nightly from cron.