// blog

How to Install and Configure Ansible on a Linux VPS: Complete Guide to Infrastructure Automation

September 02, 2026 · by Alex M.

How to Install and Configure Ansible on a Linux VPS: Complete Guide to Infrastructure Automation

If you manage multiple servers or want to eliminate repetitive tasks from your workflow, Ansible is the tool you need. It is an open-source, agentless automation platform (no agent installation required on managed servers) and incredibly easy to learn. In this guide, you'll learn how to install and configure Ansible on a NVMe VPS or SSD VPS, with practical examples throughout.

What is Ansible and Why Use It?

Ansible is an IT automation platform developed by Red Hat that lets you:

  • Provision new servers in minutes
  • Configure services (Nginx, Apache, MySQL, etc.) identically across dozens of machines
  • Deploy applications without manual intervention
  • Orchestrate complex operational workflows

Unlike Puppet or Chef, Ansible requires no agent on nodes. Communication happens over SSH, making it ideal for any Linux VPS or dedicated server.

Ansible Architecture

  • Control Node – the machine from which you run Ansible (your main VPS)
  • Managed Nodes – the servers you administer
  • Inventory – the list of managed servers
  • Playbook – the YAML file describing what operations to execute
  • Module – the basic unit of action (e.g., apt, copy, service)

Prerequisites

Before you begin, make sure you have:

  1. A VPS running Ubuntu 22.04 or Debian 12 (control node)
  2. Root access or a user with sudo privileges
  3. SSH key authentication configured
  4. Python 3 installed (comes pre-installed on Ubuntu 22.04)

If you don't have a server yet, you can quickly spin up a CLIQHOST NVMe VPS with Ubuntu pre-installed.

Step 1: Installing Ansible on the Control Node

On Ubuntu 22.04 / Debian 12

sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install -y ansible

Verify the Installation

ansible --version

Expected output:

ansible [core 2.16.x]
  python version = 3.10.x
  jinja version = 3.x.x

On CentOS / AlmaLinux 9

sudo dnf install -y epel-release
sudo dnf install -y ansible

Step 2: Configuring the Inventory File

The inventory is the heart of any Ansible configuration. It defines which servers will be managed and how they are grouped.

Simple Inventory (/etc/ansible/hosts)

[webservers]
192.168.1.10
192.168.1.11

[dbservers]
192.168.1.20

[all:vars]
ansible_user=ubuntu
ansible_ssh_private_key_file=~/.ssh/id_rsa

Create an inventory.yml file:

all:
  children:
    webservers:
      hosts:
        web01:
          ansible_host: 192.168.1.10
        web02:
          ansible_host: 192.168.1.11
    dbservers:
      hosts:
        db01:
          ansible_host: 192.168.1.20
  vars:
    ansible_user: ubuntu
    ansible_ssh_private_key_file: ~/.ssh/id_rsa

Test the Connection

ansible all -m ping -i inventory.yml

Success response:

web01 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

Step 3: Configuring ansible.cfg

Create an ansible.cfg file in your project directory:

[defaults]
inventory = ./inventory.yml
remote_user = ubuntu
private_key_file = ~/.ssh/id_rsa
host_key_checking = False
retry_files_enabled = False

[privilege_escalation]
become = True
become_method = sudo
become_user = root

Note: host_key_checking = False is useful in test environments. In production, on a managed dedicated server, keep this enabled for security.

Step 4: Your First Ad-Hoc Commands

Ad-hoc commands are useful for quick actions without writing a playbook:

# Check uptime on all servers
ansible all -m command -a "uptime"

# Install a package on all web servers
ansible webservers -m apt -a "name=nginx state=present" --become

# Copy a file to nodes
ansible webservers -m copy -a "src=/local/file.conf dest=/etc/nginx/file.conf"

# Restart a service
ansible webservers -m service -a "name=nginx state=restarted" --become

# Check disk space
ansible all -m shell -a "df -h"

Step 5: Writing Your First Playbook

A playbook is a YAML file that describes the desired state of your systems. Here is a complete example for installing and configuring Nginx:

---
- name: Install and configure Nginx
  hosts: webservers
  become: true

  vars:
    nginx_port: 80
    site_name: "example.com"

  tasks:
    - name: Update APT cache
      apt:
        update_cache: yes
        cache_valid_time: 3600

    - name: Install Nginx
      apt:
        name: nginx
        state: present

    - name: Copy site configuration
      template:
        src: templates/nginx_site.conf.j2
        dest: "/etc/nginx/sites-available/{{ site_name }}"
        owner: root
        group: root
        mode: '0644'
      notify: Restart Nginx

    - name: Enable site
      file:
        src: "/etc/nginx/sites-available/{{ site_name }}"
        dest: "/etc/nginx/sites-enabled/{{ site_name }}"
        state: link
      notify: Restart Nginx

    - name: Start and enable Nginx
      service:
        name: nginx
        state: started
        enabled: yes

  handlers:
    - name: Restart Nginx
      service:
        name: nginx
        state: restarted

Jinja2 Template for Nginx (templates/nginx_site.conf.j2)

server {
    listen {{ nginx_port }};
    server_name {{ site_name }};

    root /var/www/{{ site_name }};
    index index.html index.php;

    access_log /var/log/nginx/{{ site_name }}_access.log;
    error_log  /var/log/nginx/{{ site_name }}_error.log;

    location / {
        try_files $uri $uri/ =404;
    }
}

Running the Playbook

# Dry-run (check without making changes)
ansible-playbook playbook_nginx.yml --check

# Actual run
ansible-playbook playbook_nginx.yml

# With verbose output
ansible-playbook playbook_nginx.yml -v

Step 6: Organising Your Project with Ansible Roles

For larger projects, roles enable code reuse and organisation. Role structure:

roles/
  nginx/
    tasks/
      main.yml
    handlers/
      main.yml
    templates/
      nginx_site.conf.j2
    defaults/
      main.yml
    vars/
      main.yml
    files/
    meta/
      main.yml

Auto-generate the Structure

ansible-galaxy init roles/nginx
ansible-galaxy init roles/mariadb
ansible-galaxy init roles/php

Playbook Using Roles

---
- name: Configure LEMP stack
  hosts: webservers
  become: true
  roles:
    - nginx
    - mariadb
    - php

Step 7: Variables and Ansible Vault

Per-group Variable Files

group_vars/
  webservers.yml
  dbservers.yml
host_vars/
  web01.yml

group_vars/webservers.yml:

nginx_worker_processes: 4
nginx_worker_connections: 1024
php_version: "8.2"

Ansible Vault – Protecting Secrets

# Create an encrypted file
ansible-vault create group_vars/all/vault.yml

# Edit an encrypted file
ansible-vault edit group_vars/all/vault.yml

# Run playbook with vault
ansible-playbook site.yml --ask-vault-pass
# or with password file
ansible-playbook site.yml --vault-password-file ~/.vault_pass

vault.yml (decrypted content):

db_root_password: "SuperPassword123!"
db_app_password: "AppPassword456!"
api_key: "sk-xxxxxxxxxxxx"

Step 8: Advanced Practical Examples

Playbook for Initial VPS Hardening

---
- name: Initial VPS security hardening
  hosts: all
  become: true

  tasks:
    - name: Upgrade the system
      apt:
        upgrade: dist
        update_cache: yes

    - name: Install security packages
      apt:
        name:
          - ufw
          - fail2ban
          - unattended-upgrades
        state: present

    - name: UFW - allow SSH
      ufw:
        rule: allow
        port: "22"
        proto: tcp

    - name: UFW - allow HTTP/HTTPS
      ufw:
        rule: allow
        port: "{{ item }}"
        proto: tcp
      loop:
        - "80"
        - "443"

    - name: Enable UFW
      ufw:
        state: enabled
        policy: deny

    - name: Start and enable fail2ban
      service:
        name: fail2ban
        state: started
        enabled: yes

    - name: Disable root SSH login
      lineinfile:
        path: /etc/ssh/sshd_config
        regexp: '^PermitRootLogin'
        line: 'PermitRootLogin no'
      notify: Restart SSH

  handlers:
    - name: Restart SSH
      service:
        name: ssh
        state: restarted

Production Best Practices

  1. Always use --check before running on production servers
  2. Version-control your playbooks with Git for full traceability
  3. Separate environments (dev/staging/prod) with distinct inventories
  4. Test roles with Molecule before deployment
  5. Encrypt all secrets with Ansible Vault
  6. Document every playbook with comments and descriptive name fields

For complex production infrastructures, a managed dedicated server or a server management service can save you considerable time and effort.

Complete Ansible Project Structure

ansible-project/
├── ansible.cfg
├── inventory/
│   ├── production/
│   │   ├── hosts.yml
│   │   └── group_vars/
│   └── staging/
│       ├── hosts.yml
│       └── group_vars/
├── roles/
│   ├── common/
│   ├── nginx/
│   ├── mariadb/
│   └── php/
├── playbooks/
│   ├── site.yml
│   ├── deploy.yml
│   └── security.yml
└── README.md

Conclusion

Ansible transforms server administration from a manual, repetitive activity into an automated, reproducible, and documented process. With a handful of well-written playbooks, you can configure dozens of servers identically in minutes, eliminate human error, and always have a clear picture of your infrastructure's state.

To put everything you've learned into practice, you need a solid infrastructure beneath you. Explore CLIQHOST NVMe VPS plans — fast, reliable, and ready to run Ansible in seconds — or get in touch with our team for a tailored server management plan. You'll also find more Linux and DevOps guides on the CLIQHOST blog.

SHARE
// what clients say

What Our Clients Say

Real reviews from customers who trust CLIQHOST for performance, reliability and expert technical support.

★★★★★

"We moved our online shop from a foreign host and the difference is night and day — pages load instantly and support replies in minutes, in Romanian."

AM
Andrei M.
eCommerce owner · Chișinău
★★★★★

"Migrated 12 client sites to CLIQHOST. Free migration, zero downtime, and the cPanel setup is exactly what my team needed. Highly recommend."

EV
Elena V.
Web agency · Bălți
★★★★★

"Our NVMe VPS handles traffic spikes without a sweat. Full root, local datacenter, and billing in MDL — everything we wanted from a provider."

DC
Dmitri C.
SaaS founder · Chișinău