Ansible
Ansible is an infrastructure-as-code (IaC) tool for configuration management and automated deployments. Use it to install packages, manage config files, create users, and apply firewall rules across all your servers with a single command.
Unlike tools such as Chef and Puppet, Ansible does not require an agent on each client. It connects over SSH, which reduces CPU and RAM overhead and scales better across large environments. A dedicated ansible user account on each managed server handles remote execution.
Ansible reads server addresses from an inventory file. You can store the inventory file in a git repository and use pull mode to have each server configure itself. Client machines require OpenSSH.
Setup
Complete this one-time setup on the central server and each client before using Ansible:
- Create the
ansibleuser on the central server and all clients:adduser ansible - Set up passwordless SSH from the central server to each client:
ssh-copy-id -i ~/.ssh/id_rsa.pub ansible@<client-ip> - Grant the
ansibleuser passwordless sudo privileges:Add:vim /etc/sudoers.d/ansibleThen set the correct ownership and permissions:ansible ALL=(ALL) NOPASSWD: ALLchown root:root /etc/sudoers.d/ansible chmod 440 /etc/sudoers.d/ansible - Test that the central server can connect and run sudo commands on a client:
ssh <client-ip> sudo ls /etc
Inventory files
The inventory file tells Ansible which servers to manage. The configuration file (ansible.cfg) tells Ansible where to find the inventory and which user to connect as. Ansible reads ansible.cfg from the current directory each time it runs. You can store it in a git repository, but avoid including sensitive information such as passwords or private keys.
To create the inventory and configuration files:
- Create the configuration directory:
mkdir /etc/ansible - Create the inventory file and set ownership:
touch /etc/ansible/hosts chown ansible /etc/ansible/hosts chmod 600 /etc/ansible/hosts - Add server IP addresses or DNS names to the inventory file:
vim /etc/ansible/hosts - Create the configuration file:Add:
vim /etc/ansible/ansible.cfg[defaults] inventory = /etc/ansible/hosts # path to inventory file remote_user = ansible # default user for playbooks — must exist on client machines - Test that Ansible can connect to all clients:
ansible all -m ping
Playbooks
A playbook is a YAML file containing Ansible instructions. Each individual instruction is a play. Playbooks use built-in modules to perform tasks — ansible.builtin.apt wraps the apt package manager, and ansible.builtin.dnf wraps dnf. You can also configure a cron job to pull and apply playbook changes from a remote repository on a schedule.
Run a playbook with:
ansible-playbook <playbook.yml>
Install a single package
---
- hosts: all
become: true
tasks:
- name: Install htop
ansible.builtin.apt:
name: htop
Install multiple packages
---
- hosts: all
become: true
tasks:
- name: Install multiple packages
ansible.builtin.apt:
name:
- git
- vim-nox
- htop
Copy a file
---
- hosts: all
become: true
tasks:
- name: Copy SSH motd
ansible.builtin.copy:
src: motd
dest: /etc/motd
Web server playbook
This playbook installs Apache, starts and enables the service, and deploys an HTML index file. Use it as a starting point for provisioning web servers across your inventory:
---
- hosts: all
become: true
tasks:
- name: Install Apache
ansible.builtin.apt:
name: apache2
- name: Start the apache2 service
ansible.builtin.service:
name: apache2
state: started
enabled: true
- name: Copy index.html
ansible.builtin.copy:
src: index.html
dest: /var/www/html/index.html
Common tasks
These playbook examples cover frequently repeated administration tasks.
Install security updates
---
- hosts: all
become: true
tasks:
- name: Install security updates
ansible.builtin.apt:
upgrade: dist
update_cache: true
Create a user account
---
- hosts: all
become: true
tasks:
- name: Create user account
ansible.builtin.user:
name: <username>
state: present
Set a password
---
- hosts: all
become: true
tasks:
- name: Set user password
ansible.builtin.user:
name: <username>
password: "{{ '<password>' | password_hash('sha512') }}"
Enable a service at boot
---
- hosts: all
become: true
tasks:
- name: Enable service at boot
ansible.builtin.service:
name: <service>
enabled: true
Pull method
The inventory file approach works well in small environments with static IP addresses. In dynamic environments such as the cloud, where machines are regularly provisioned and decommissioned, pull mode is more practical.
In pull mode, there is no central server. Each server runs Ansible against itself, pulling its playbook from a git repository. This makes pull mode well-suited for cloud instances that configure themselves on first boot.
Key requirements for pull mode:
- Store playbooks in a git repository accessible from all managed servers.
- Name the main playbook
local.yml. If you use a different name, specify it in the command. - Set
hosts:tolocalhost— commands run locally, not over SSH. - Run as the
ansibleuser or a user with sudo privileges, since commands are non-interactive.
The -o flag skips execution if the repository has not changed since the last run:
ansible-pull -U https://github.com/<username>/<repo-name>.git # pull and apply the playbook
ansible-pull -o -U https://github.com/<username>/<repo-name>.git # only run if the repo has changed
Sample pull mode playbook
---
- hosts: localhost
become: true
tasks:
- name: Install Apache
ansible.builtin.apt:
name: apache2
- name: Start the apache2 service
ansible.builtin.service:
name: apache2
state: started
enabled: true
- name: Copy index.html
ansible.builtin.copy:
src: index.html
dest: /var/www/html/index.html