Ansible is an open-source automation tool written in Python that handles configuration management, application deployment, and task orchestration over SSH – no agents needed on managed nodes. It uses simple YAML playbooks to define desired state, making it the go-to choice for Linux system administrators managing fleets of servers.
This guide covers installing Ansible on Debian 13 (Trixie) using two methods – pip in a virtual environment (recommended) and the default APT repository. We also walk through inventory configuration, ad-hoc commands, writing a basic playbook, and ansible-vault for secrets management.
Prerequisites
- A Debian 13 server as the control node (where Ansible runs)
- One or more managed nodes (any Linux distribution) reachable over SSH
- A user with sudo privileges on all servers
- SSH key-based authentication configured between the control node and managed nodes
Our lab setup for this guide:
| Role | OS | IP Address |
|---|---|---|
| Control Node | Debian 13 | 192.168.1.10 |
| Managed Node 1 | Debian 13 | 192.168.1.11 |
| Managed Node 2 | Rocky Linux 10 | 192.168.1.12 |
Step 1: Install Ansible on Debian 13 via pip (Recommended)
Installing Ansible through pip gives you the latest version and keeps it isolated from system packages. Debian 13 ships with Python 3.13, which works well with the current Ansible release.
Install the required packages first:
sudo apt update
sudo apt install python3 python3-pip python3-venv sshpass -y
Create a Python virtual environment
Always install Ansible inside a virtual environment to avoid conflicts with system Python packages. Running pip install globally is deprecated on Debian 13 and will fail without --break-system-packages.
python3 -m venv ~/ansible-venv
Activate the virtual environment:
source ~/ansible-venv/bin/activate
Install Ansible with pip
With the venv active, install Ansible:
pip install ansible
This installs the full Ansible package (currently version 13.4.0) which includes ansible-core 2.20 and all standard collections.
Verify the installation:
$ ansible --version
ansible [core 2.20.0]
config file = None
configured module search path = ['/home/user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /home/user/ansible-venv/lib/python3.13/site-packages/ansible
ansible collection location = /home/user/.ansible/collections:/usr/share/ansible/collections
executable location = /home/user/ansible-venv/bin/ansible
python version = 3.13.5 (main, ...) [GCC 14.2.0]
jinja version = 3.1.6
libyaml = True
To make the venv available every time you log in, add the activation line to your shell profile:
echo 'source ~/ansible-venv/bin/activate' >> ~/.bashrc
Step 2: Install Ansible on Debian 13 from APT Repository
If you prefer the system package manager, Ansible is available directly from the Debian 13 repository. The repo version (12.0.0 at the time of writing) may be slightly behind the latest pip release, but it is well-tested and integrates with system updates.
sudo apt update
sudo apt install ansible -y
Verify the installation:
$ ansible --version
ansible [core 2.17.7]
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/user/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
ansible collection location = /home/user/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/bin/ansible
python version = 3.13.5 (main, ...) [GCC 14.2.0]
jinja version = 3.1.4
libyaml = True
The APT method also installs /etc/ansible/ansible.cfg and /etc/ansible/hosts as default configuration and inventory files.
Step 3: Set Up SSH Key Authentication
Ansible connects to managed nodes over SSH. Key-based authentication removes the need to type passwords on every run. If you already have SSH keys configured, skip to Step 4.
Generate an SSH key pair on the control node:
ssh-keygen -t ed25519 -C "ansible-control"
Press Enter to accept the defaults. Then copy the public key to each managed node:
ssh-copy-id [email protected]
ssh-copy-id [email protected]
Verify passwordless login works:
ssh [email protected] "hostname"
ssh [email protected] "hostname"
Both commands should return the hostname of the remote server without prompting for a password.
Step 4: Configure Ansible Inventory
The inventory file defines which hosts Ansible manages and how they are grouped. Create a project directory and an inventory file:
mkdir -p ~/ansible-project
vi ~/ansible-project/inventory.ini
Add your managed nodes with their connection details:
[webservers]
192.168.1.11 ansible_user=user
[dbservers]
192.168.1.12 ansible_user=user
[all:vars]
ansible_python_interpreter=/usr/bin/python3
The [all:vars] section sets the Python interpreter path for all hosts. This avoids discovery warnings on managed nodes.
You can also create an ansible.cfg file in the project directory to set defaults. This is useful for managing tasks from a web-based interface like Semaphore or from the command line.
vi ~/ansible-project/ansible.cfg
Add the following configuration:
[defaults]
inventory = inventory.ini
remote_user = user
host_key_checking = False
retry_files_enabled = False
Verify your inventory is parsed correctly:
cd ~/ansible-project
ansible-inventory --list
Step 5: Run Ansible Ad-Hoc Commands
Ad-hoc commands let you run quick one-off tasks without writing a playbook. They are useful for checking system state, restarting services, or pushing small changes across multiple servers.
Test connectivity to all managed nodes:
$ ansible all -m ping
192.168.1.11 | SUCCESS => {
"changed": false,
"ping": "pong"
}
192.168.1.12 | SUCCESS => {
"changed": false,
"ping": "pong"
}
The ping module does not send ICMP packets – it verifies that Ansible can connect via SSH and execute Python on the remote host.
Check disk usage on the webservers group:
ansible webservers -m shell -a "df -h /"
Check memory on all hosts:
ansible all -m shell -a "free -m"
Check uptime across all nodes:
ansible all -m shell -a "uptime"
Install a package on Debian-based managed nodes using the apt module:
ansible webservers -m apt -a "name=curl state=present" --become
The --become flag runs the command with sudo on the remote host. You can automate many repetitive tasks this way without writing full playbooks.
Here is a quick reference of common ad-hoc patterns:
| Task | Command |
|---|---|
| Ping all hosts | ansible all -m ping |
| Check uptime | ansible all -m shell -a "uptime" |
| Restart a service | ansible all -m service -a "name=nginx state=restarted" --become |
| Copy a file | ansible all -m copy -a "src=./file.txt dest=/tmp/file.txt" |
| Gather facts | ansible all -m setup |
Step 6: Write and Run an Ansible Playbook
Playbooks define a series of tasks in YAML format. They are reusable, version-controllable, and the primary way to use Ansible in production.
Create a playbook that installs Nginx on the webservers group, enables the service, and verifies it is running:
vi ~/ansible-project/setup-nginx.yml
Add the following content:
---
- name: Install and configure Nginx
hosts: webservers
become: yes
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install Nginx
apt:
name: nginx
state: present
- name: Start and enable Nginx
systemd:
name: nginx
state: started
enabled: yes
- name: Verify Nginx is running
shell: systemctl is-active nginx
register: nginx_status
- name: Show Nginx status
debug:
var: nginx_status.stdout
Run the playbook:
cd ~/ansible-project
ansible-playbook setup-nginx.yml
Expected output:
PLAY [Install and configure Nginx] ********************************************
TASK [Gathering Facts] ********************************************************
ok: [192.168.1.11]
TASK [Update apt cache] *******************************************************
changed: [192.168.1.11]
TASK [Install Nginx] **********************************************************
changed: [192.168.1.11]
TASK [Start and enable Nginx] *************************************************
ok: [192.168.1.11]
TASK [Verify Nginx is running] ************************************************
changed: [192.168.1.11]
TASK [Show Nginx status] ******************************************************
ok: [192.168.1.11] => {
"nginx_status.stdout": "active"
}
PLAY RECAP ********************************************************************
192.168.1.11 : ok=6 changed=3 unreachable=0 failed=0 skipped=0
Every task reports whether it changed something or was already in the desired state. This idempotency is what makes Ansible safe to run repeatedly.
Use variables and handlers
A more realistic playbook uses variables for flexibility and handlers to restart services only when configuration changes:
vi ~/ansible-project/deploy-app.yml
Add the following content:
---
- name: Deploy web application
hosts: webservers
become: yes
vars:
http_port: 8080
app_root: /var/www/myapp
tasks:
- name: Install required packages
apt:
name:
- nginx
- curl
state: present
update_cache: yes
- name: Create application directory
file:
path: "{{ app_root }}"
state: directory
owner: www-data
group: www-data
mode: '0755'
- name: Deploy Nginx vhost config
template:
src: templates/vhost.conf.j2
dest: /etc/nginx/sites-available/myapp.conf
notify: Reload Nginx
- name: Enable site
file:
src: /etc/nginx/sites-available/myapp.conf
dest: /etc/nginx/sites-enabled/myapp.conf
state: link
notify: Reload Nginx
handlers:
- name: Reload Nginx
systemd:
name: nginx
state: reloaded
Handlers run only when notified by a task that reports “changed” – Nginx reloads only when the config file actually changes.
Step 7: Manage Secrets with Ansible Vault
Ansible Vault encrypts sensitive data – passwords, API keys, certificates – so you can store them safely in version control alongside your playbooks. For a complete reference, see our Ansible Vault cheat sheet.
Create an encrypted file
ansible-vault create ~/ansible-project/secrets.yml
You will be prompted to set a vault password. The file opens in your editor. Add your sensitive variables:
db_password: S3cur3P@ssw0rd
api_key: abc123def456
Save and close. The file is now AES-256 encrypted on disk.
View and edit encrypted files
View the contents without editing:
ansible-vault view ~/ansible-project/secrets.yml
Edit the encrypted file:
ansible-vault edit ~/ansible-project/secrets.yml
Use vault secrets in playbooks
Reference vault-encrypted files with vars_files in your playbook:
vi ~/ansible-project/db-setup.yml
Add the following content:
---
- name: Configure database
hosts: dbservers
become: yes
vars_files:
- secrets.yml
tasks:
- name: Set database password
shell: "echo 'Password configured: {{ db_password }}'"
no_log: true
Run the playbook with the vault password:
ansible-playbook db-setup.yml --ask-vault-pass
For automation (CI/CD pipelines), store the vault password in a file and reference it:
echo 'your-vault-password' > ~/.vault_pass
chmod 600 ~/.vault_pass
ansible-playbook db-setup.yml --vault-password-file ~/.vault_pass
Encrypt an existing file
If you have a plain-text file with secrets that needs encryption:
ansible-vault encrypt ~/ansible-project/vars/credentials.yml
To decrypt it back to plain text:
ansible-vault decrypt ~/ansible-project/vars/credentials.yml
To change the vault password on an encrypted file:
ansible-vault rekey ~/ansible-project/secrets.yml
Conclusion
Ansible is installed on Debian 13 and ready to manage your infrastructure. We covered pip-based installation in a virtual environment, APT installation, inventory setup, ad-hoc commands, playbook creation, and vault-based secrets management.
For production use, set up a dedicated Ansible AWX instance for centralized job scheduling and RBAC, keep your playbooks in Git, and use ansible-vault for all credentials. Structure larger projects with roles to keep tasks modular and reusable across environments.