Automate Server Deployments with Ansible & GitOps: A Developer’s Guide
Why Automate Server Deployments?
In today’s fast-moving development cycles, manual server deployments are not just inefficient — they’re risky. Human error, inconsistent environments, and deployment downtime can cripple your CI/CD pipeline. That’s where Ansible and GitOps come in.
Ansible is an open-source configuration management and automation tool that lets you define infrastructure as code (IaC), and GitOps is a methodology that treats infrastructure and applications as code stored in version-controlled repositories — enabling declarative, auditable, and automated deployments.
Together, they form a powerhouse for automated, repeatable, and scalable server deployments — no more “I’ll just SSH into the server and run it again” workflows.
What You’ll Learn
By the end of this post, you’ll know:
- How to set up Ansible for server automation.
- How to integrate Ansible with GitOps for declarative deployments.
- How to automate deployments using Git hooks and CI/CD.
- How to manage server state, rollback, and auditing.
- Best practices for production-grade automation.
Setting Up Ansible
Step 1: Install Ansible
First, install Ansible on your control machine (e.g., your local laptop or CI server):
# On Ubuntu/Debian
sudo apt update
sudo apt install ansible
# On macOS with Homebrew
brew install ansible
Verify installation:
ansible --version
Step 2: Create Your First Playbook
A playbook is Ansible’s core automation configuration file — written in YAML.
Create a simple playbook site.yml:
---
- hosts: webservers
become: yes
tasks:
- name: Install Nginx
apt:
name: nginx
state: present
- name: Start Nginx
service:
name: nginx
state: started
enabled: yes
✅ Note: Replace
webserverswith your actual inventory group (more on that next).
Step 3: Define Your Inventory
Ansible uses an inventory file to know which servers to target. Create inventory.ini:
[webservers]
server1 ansible_host=192.168.1.10
server2 ansible_host=192.168.1.11
💡 Tip: Use dynamic inventory if managing dozens or hundreds of servers.
Step 4: Run Your Playbook
ansible-playbook site.yml -i inventory.ini
You’ll see Ansible connect to each server and execute tasks — no manual SSH required.
Introducing GitOps: The Declarative Way
GitOps is not just a buzzword — it’s a workflow. It says: “The system should always be in the desired state defined by code in Git.”
GitOps Principles
- Infrastructure as Code (IaC) — All server configurations live in Git.
- Declarative — You declare what you want, not how to get it.
- Automated Sync — Any change to Git triggers a sync to the server.
- Auditable — Every change is versioned, reviewed, and rollable.
How GitOps Fits with Ansible
Ansible is perfect for GitOps:
- Playbooks = Configuration code
- Git = State definition
- CI/CD = Trigger sync
Integrating Ansible with GitOps
Step 1: Store Playbooks in Git
Create a repo for your infrastructure:
mkdir ansible-gitops && cd ansible-gitops
git init
git remote add origin https://github.com/yourname/ansible-gitops.git
Add your playbooks and inventory:
mkdir playbooks inventory
cp site.yml playbooks/
cp inventory.ini inventory/
Commit:
git add .
git commit -m "Initial Ansible playbooks"
git push origin main
Step 2: Use a CI/CD Tool to Trigger Deployments
Popular choices: GitHub Actions, GitLab CI, CircleCI, or ArgoCD (for more advanced GitOps).
Here’s a simple GitHub Actions workflow:
# .github/workflows/deploy.yml
name: Deploy Servers
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install Ansible
run: |
sudo apt update && sudo apt install ansible -y
- name: Deploy with Ansible
run: |
ansible-playbook playbooks/site.yml -i inventory/inventory.ini --become
🚀 This workflow runs every time
mainis pushed — automatically deploying your server configuration.
Step 3: Use Tags for Different Environments
Ansible supports tags, allowing you to run only specific tasks — ideal for staging, prod, etc.
Example: site.yml
---
- hosts: webservers
become: yes
tags: [nginx, firewall]
tasks:
- name: Install Nginx
apt:
name: nginx
state: present
- name: Start Nginx
service:
name: nginx
state: started
enabled: yes
Run only nginx tasks:
ansible-playbook site.yml --tags=nginx -i inventory.ini
Step 4: Handle Secrets Securely
Never store passwords or keys in Git! Use Ansible Vault:
ansible-vault create group_vars/common/vault.yml
In vault.yml:
ansible_user: myuser
ansible_password: mypassword
Use it in your playbook:
- name: Use vault vars
ansible.builtin.lineinfile:
path: /etc/ansible/credentials
line: "{{ ansible_password }}"
state: present
Run with vault:
ansible-playbook site.yml --vault-password-file ~/.vault_pass
Advanced GitOps with ArgoCD (Optional)
For more complex environments, consider ArgoCD — a powerful GitOps operator for Kubernetes. While this post focuses on servers, you can extend Ansible + GitOps to Kubernetes using ArgoCD.
ArgoCD polls your Git repo for changes and applies them via Ansible or Helm — automating infrastructure across layers.
Best Practices for Production Deployments
✅ Use Ansible Roles — Modularize your playbooks.
mkdir roles/nginx
cp site.yml roles/nginx/tasks/main.yml
✅ Idempotency — Ansible is idempotent — running the same playbook twice won’t break your system.
✅ Testing — Use ansible-test or molecule to test playbooks in a sandbox.
✅ Rollback — If something goes wrong, revert to the last known good state via Git tags or branches.
✅ Logging & Monitoring — Enable ansible-playbook --log-path and integrate with ELK stack or Grafana.
✅ Limit Permissions — Use SSH keys and restricted user roles — never run Ansible as root in production.
✅ Encrypt Secrets — Always use Ansible Vault for sensitive data.
Real-World Example: Deploying a Django App
Let’s say you want to deploy a Django app on Ubuntu servers.
Playbook: deploy_django.yml
---
- hosts: webapp_servers
become: yes
tasks:
- name: Install Python 3.10
apt:
name: python3.10
state: present
- name: Install pip
apt:
name: python3-pip
state: present
- name: Install system dependencies
apt:
name:
- nginx
- postgresql
- python3-venv
state: present
- name: Create app user
user:
name: djangoapp
state: present
- name: Clone app repo
git:
repo: https://github.com/yourname/django-app.git
dest: /home/djangoapp/app
version: main
- name: Create virtualenv
command: python3.10 -m venv /home/djangoapp/app/venv
- name: Install dependencies
command: /home/djangoapp/app/venv/bin/pip3 install -r /home/djangoapp/app/requirements.txt
- name: Configure Nginx
template:
src: templates/nginx.conf.j2
dest: /etc/nginx/sites-available/app.conf
notify: Restart nginx
- name: Enable Nginx site
file:
path: /etc/nginx/sites-enabled/app.conf
state: link
src: /etc/nginx/sites-available/app.conf
- name: Restart nginx
service:
name: nginx
state: restarted
📝 Use Jinja2 templates for dynamic configs, e.g.,
{{ app_host }},{{ db_url }}.
Troubleshooting Common Issues
❌ “Connection refused”
- Check SSH keys and firewall rules.
- Use
ansible inventory --listto verify inventory. - Test connection:
ansible -m ping -i inventory.ini webservers
❌ “Module not found”
- Check if module is available:
ansible-doc -l | grep module_name - Install modules via
pipif needed.
❌ “Permission denied”
- Use
become: yesand ensure user has sudo rights. - Use
ansible --become-method=sudo --ask-become-pass
❌ “Playbook failed on some hosts”
- Use
--checkto test without applying changes. - Use
ansible-playbook --diffto see what will change.
Conclusion: Automate with Confidence
Automating server deployments with Ansible and GitOps isn’t just a productivity hack — it’s a best practice for modern DevOps.
You’ve learned:
- How to write idempotent, reusable Ansible playbooks
- How to store infrastructure in Git and automate sync
- How to integrate with CI/CD and handle secrets securely
- How to extend to complex environments with ArgoCD
Next time you deploy, you won’t need to SSH into servers — you’ll just push to Git and let automation handle the rest.
🧠 Pro Tip: Always keep your
ansible.cfgfile in.gitignore— avoid leaking credentials or configs.
Ready to Automate?
Start small — automate one server, then scale. Use Ansible and GitOps together to move from “I deploy manually” to “I deploy automatically — and confidently.”
🚀 Your infrastructure is code. Your deployments are versioned. Your ops are automated.
Resources
Stay Connected
Follow for more DevOps deep dives, infrastructure automation, and CI/CD workflows.
— Yash