Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Ansible Ubuntu
GUIDEInstall Guides

How to Install Ansible on Ubuntu 24.04 — Configuration Management

16 min read

How to Install Ansible on Ubuntu 24.04 — Configuration Management

If you manage more than two or three servers, manual SSH sessions stop scaling. You forget which box got the latest patch, config drift creeps in, and onboarding a new team member becomes a ritual of tribal knowledge. Ansible solves this by codifying every change into a version-controlled playbook that runs the same way, every time, across your whole fleet.

This guide walks you through installing Ansible on Ubuntu 24.04 LTS, writing your first inventory and playbook, and growing into production patterns like roles, Vault, and dynamic inventories.

What Is Ansible?

Ansible is an open-source configuration management, application deployment, and orchestration tool originally created by Michael DeHaan in 2012 and now maintained by Red Hat. It lets you describe the desired state of your infrastructure in simple YAML files called playbooks, and then applies that state across any number of machines.

Three things make Ansible stand out:

  • Agentless. There is no daemon to install on target hosts. Ansible pushes changes over SSH (or WinRM for Windows), using only Python on the target. If you can SSH to a box, you can manage it with Ansible.
  • Idempotent. Running the same playbook twice produces the same result. Tasks check the current state before making changes — installing a package that is already installed is a no-op, not an error.
  • Human-readable. Playbooks are plain YAML. You don't need to learn a domain-specific language; a sysadmin with basic Linux knowledge can read an Ansible playbook and understand what it does.
  • Why agentless matters

    Competing tools like Puppet and Chef require a persistent agent on every managed node, plus a central server (Puppet Master, Chef Server) that agents check into. That means:

    • Extra daemons consuming RAM and CPU on every VPS.
    • Firewall rules to allow agent-to-master traffic.
    • Certificates to rotate, agent versions to upgrade.
    • A chicken-and-egg problem: how do you install the agent in the first place?
    Ansible sidesteps all of this. You install it on one machine — your laptop, a jump host, or a CI runner — and that machine pushes changes out via SSH. The target only needs Python (Ubuntu 24.04 ships with Python 3.12 by default) and an SSH server.

    Prerequisites

    Before you start, make sure you have:

    • An Ubuntu 24.04 LTS machine to act as the control node (your laptop works too).
    • SSH access to one or more target nodes. A CloudCore Starter VPS is a great candidate.
    • A non-root user with sudo privileges on both control and target nodes.
    • Python 3.10+ on the control node (Ubuntu 24.04 ships with 3.12).
    Update package lists before proceeding:

    bash
    sudo apt update && sudo apt upgrade -y

    Installation Method 1: Ansible PPA (Recommended for Latest)

    The official Ansible PPA ships the newest stable release, which is usually a few versions ahead of the Ubuntu archive. This is the path we recommend for most users.

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

    Verify the install:

    bash
    ansible --version

    You should see output similar to:

    text
    ansible [core 2.17.x]
      config file = /etc/ansible/ansible.cfg
      configured module search path = ['/home/user/.ansible/plugins/modules', ...]
      ansible python module location = /usr/lib/python3/dist-packages/ansible
      python version = 3.12.x

    Installation Method 2: pip3 in a Virtual Environment

    If you need a specific Ansible version, want to isolate it per project, or are working on a shared host where you don't have sudo, install Ansible into a Python virtual environment.

    bash
    sudo apt install -y python3-venv python3-pip
    python3 -m venv ~/.venvs/ansible
    source ~/.venvs/ansible/bin/activate
    pip install --upgrade pip
    pip install ansible

    To activate the environment in future sessions:

    bash
    source ~/.venvs/ansible/bin/activate

    This gives you full control over the version:

    bash
    pip install 'ansible==9.5.1'

    Installation Method 3: apt (Ubuntu Archive)

    The version in the default Ubuntu 24.04 archive is older but perfectly usable for most needs:

    bash
    sudo apt install -y ansible

    Check the version — you'll typically get a release that is 6–12 months behind upstream. Fine for learning, but we recommend the PPA for production.

    Verifying the Installation

    Regardless of install method, confirm Ansible is functional:

    bash
    ansible --version
    ansible localhost -m ping

    The ping module (which doesn't actually send ICMP — it just verifies Python works on the target) should return:

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

    Your First Inventory File

    An inventory tells Ansible which hosts to manage and how to group them. The default location is /etc/ansible/hosts, but project-local inventories (./inventory.ini) are considered best practice.

    Create /etc/ansible/hosts:

    ini
    # /etc/ansible/hosts

    [webservers] web1.example.com web2.example.com ansible_host=203.0.113.45

    [dbservers] db1.example.com ansible_port=2222

    [production:children] webservers dbservers

    [webservers:vars] ansible_user=deploy ansible_python_interpreter=/usr/bin/python3

    Key concepts:

    • Groups ([webservers]) let you target subsets of hosts.
    • Host variables (ansible_host, ansible_port) override connection defaults.
    • Group variables ([webservers:vars]) apply to every host in the group.
    • Group of groups ([production:children]) lets you nest.
    List your inventory to confirm it parses:

    bash
    ansible-inventory --list -y

    SSH Key Setup to Targets

    Ansible uses your SSH configuration. Set up key-based auth once and every subsequent run is passwordless.

    On the control node:

    bash
    ssh-keygen -t ed25519 -C "ansible@control"
    ssh-copy-id [email protected]
    ssh-copy-id [email protected]

    Test the connection:

    bash
    ssh [email protected] 'echo connected'

    For privilege escalation (running tasks as root), ensure your deploy user has passwordless sudo:

    bash
    echo "deploy ALL=(ALL) NOPASSWD:ALL" | sudo tee /etc/sudoers.d/deploy

    Ad-hoc Commands

    Before writing playbooks, practice with one-off commands. The pattern is ansible <pattern> -m <module> -a "<args>".

    bash
    # Ping every host
    ansible all -m ping

    Check uptime on webservers

    ansible webservers -m command -a "uptime"

    Install nginx on all webservers (requires sudo: -b)

    ansible webservers -b -m apt -a "name=nginx state=present update_cache=yes"

    Restart a service

    ansible webservers -b -m service -a "name=nginx state=restarted"

    Copy a file

    ansible webservers -m copy -a "src=./index.html dest=/var/www/html/index.html"

    Gather facts (system info)

    ansible web1.example.com -m setup

    Ad-hoc commands are perfect for quick sanity checks and exploratory work. For anything you'll run more than twice, write a playbook.

    Your First Playbook: Install and Configure Nginx

    Playbooks are YAML files that describe a sequence of tasks. Create nginx.yml:

    yaml
    ---
    
    • name: Install and configure Nginx on webservers
    hosts: webservers become: yes vars: server_name: example.com document_root: /var/www/html

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

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

    - name: Ensure document root exists file: path: "{{ document_root }}" state: directory owner: www-data group: www-data mode: '0755'

    - name: Deploy index page copy: content: "<h1>Hello from {{ inventory_hostname }}</h1>" dest: "{{ document_root }}/index.html" owner: www-data group: www-data mode: '0644'

    - name: Deploy Nginx site config template: src: templates/nginx-site.conf.j2 dest: /etc/nginx/sites-available/{{ server_name }} mode: '0644' notify: Reload Nginx

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

    - name: Ensure Nginx is started and enabled service: name: nginx state: started enabled: yes

    handlers: - name: Reload Nginx service: name: nginx state: reloaded

    And templates/nginx-site.conf.j2:

    nginx
    server {
        listen 80;
        server_name {{ server_name }};
        root {{ document_root }};
        index index.html;

    access_log /var/log/nginx/{{ server_name }}.access.log; error_log /var/log/nginx/{{ server_name }}.error.log;

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

    Run it:

    bash
    ansible-playbook -i /etc/ansible/hosts nginx.yml

    Use --check for a dry run (Ansible reports what would change, without changing anything):

    bash
    ansible-playbook nginx.yml --check --diff

    Roles and Ansible Galaxy

    Once your playbooks grow beyond a handful of tasks, split them into roles. A role is a self-contained bundle of tasks, handlers, templates, files, and variables with a standard directory layout.

    Create one with:

    bash
    ansible-galaxy role init roles/webserver

    The generated structure:

    text
    roles/webserver/
    ├── defaults/main.yml    # default variables (lowest precedence)
    ├── files/               # static files to copy
    ├── handlers/main.yml    # handlers
    ├── meta/main.yml        # role metadata and dependencies
    ├── tasks/main.yml       # the actual tasks
    ├── templates/           # Jinja2 templates
    ├── tests/
    └── vars/main.yml        # role variables (higher precedence)

    Use the role from a playbook:

    yaml
    ---
    
    • hosts: webservers
    become: yes roles: - webserver - { role: firewall, firewall_allow_ports: [80, 443] }

    Installing community roles

    Ansible Galaxy (galaxy.ansible.com) hosts thousands of community roles and collections. Install one:

    bash
    ansible-galaxy role install geerlingguy.nginx
    ansible-galaxy collection install community.general

    Pin versions in requirements.yml:

    yaml
    ---
    roles:
      - name: geerlingguy.nginx
        version: 3.1.4
      - name: geerlingguy.postgresql
        version: 3.5.2

    collections: - name: community.general version: 8.6.0 - name: ansible.posix version: 1.5.4

    Install everything with:

    bash
    ansible-galaxy install -r requirements.yml

    Variables and Templating

    Ansible uses Jinja2 for templating and offers a rich variable precedence system.

    Where variables live

    • group_vars/all.yml — applies to every host.
    • group_vars/webservers.yml — applies to hosts in the webservers group.
    • host_vars/web1.example.com.yml — applies to a single host.
    • vars: block in a playbook.
    • defaults/main.yml and vars/main.yml in a role.
    • Extra vars on the command line: -e "key=value" (highest precedence).

    Example

    group_vars/webservers.yml:

    yaml
    ---
    nginx_worker_processes: auto
    nginx_worker_connections: 1024
    app_port: 8080
    environment: production

    Reference in a template:

    nginx
    worker_processes {{ nginx_worker_processes }};
    events {
        worker_connections {{ nginx_worker_connections }};
    }

    Facts

    Ansible automatically gathers host facts (CPU count, memory, IP addresses, distro version, etc.). Use them in templates and conditions:

    yaml
    - name: Configure workers based on CPU count
      template:
        src: workers.conf.j2
        dest: /etc/myapp/workers.conf
      vars:
        worker_count: "{{ ansible_processor_vcpus * 2 }}"

    Ansible Vault for Secrets

    Never commit plaintext passwords, API tokens, or private keys to Git. Ansible Vault encrypts secrets at rest with a password you control.

    Encrypt a file

    bash
    ansible-vault create group_vars/webservers/vault.yml

    You'll be prompted for a password, then dropped into $EDITOR. Put secrets inside:

    yaml
    ---
    vault_db_password: "s3cr3t-p@ssw0rd"
    vault_api_token: "tok_1a2b3c4d"

    Reference encrypted vars

    Convention: the vault file holds vault_* variables. A regular (unencrypted) file aliases them:

    group_vars/webservers/vars.yml:

    yaml
    ---
    db_password: "{{ vault_db_password }}"
    api_token: "{{ vault_api_token }}"

    Playbooks reference db_password normally; the Vault password is only required at run time.

    Run a playbook that uses Vault

    bash
    # Prompt for password
    ansible-playbook site.yml --ask-vault-pass

    Read from a password file (add to .gitignore!)

    ansible-playbook site.yml --vault-password-file ~/.vault_pass

    Per-vault-id passwords for multi-environment setups

    ansible-playbook site.yml --vault-id prod@~/.vault_prod --vault-id dev@~/.vault_dev

    Other vault operations

    bash
    ansible-vault edit group_vars/webservers/vault.yml
    ansible-vault view group_vars/webservers/vault.yml
    ansible-vault rekey group_vars/webservers/vault.yml   # change password
    ansible-vault encrypt_string 's3cr3t' --name 'db_password'

    Writing Custom Modules (Overview)

    Most of the time, existing modules cover your needs. But when they don't, you can write your own. Modules are simply executables that:

  • Receive a JSON or key=value argument string on stdin (or argv).
  • Do the work (idempotently).
  • Print a JSON result to stdout with at minimum {"changed": true/false}.
  • A minimal Python module at library/hello.py:

    python
    #!/usr/bin/python
    from ansible.module_utils.basic import AnsibleModule

    def main(): module = AnsibleModule( argument_spec=dict( name=dict(type='str', required=True), ), supports_check_mode=True, ) name = module.params['name'] module.exit_json(changed=False, msg=f"Hello, {name}!")

    if __name__ == '__main__': main()

    Use it in a task:

    yaml
    - name: Say hello
      hello:
        name: "Ansible"
      register: result

    • debug: var=result.msg
    Ansible auto-discovers modules under library/ next to your playbook. For production, bundle them in a collection.

    Dynamic Inventories (AWS, DigitalOcean, Others)

    Static inventory files don't scale when your servers spin up and down with autoscaling. Dynamic inventory plugins query your cloud provider's API and build the inventory on the fly.

    AWS EC2

    Install the AWS collection:

    bash
    ansible-galaxy collection install amazon.aws
    pip install boto3 botocore

    Create inventory.aws_ec2.yml:

    yaml
    ---
    plugin: amazon.aws.aws_ec2
    regions:
      - us-east-1
      - eu-west-1
    keyed_groups:
      - key: tags.Role
        prefix: role
      - key: placement.region
        prefix: region
    filters:
      tag:Environment: production
      instance-state-name: running
    hostnames:
      - tag:Name
      - dns-name
    compose:
      ansible_host: public_ip_address

    Run against it:

    bash
    ansible-inventory -i inventory.aws_ec2.yml --graph
    ansible-playbook -i inventory.aws_ec2.yml site.yml

    DigitalOcean

    bash
    ansible-galaxy collection install community.digitalocean
    export DO_API_TOKEN="dop_v1_..."

    inventory.digitalocean.yml:

    yaml
    ---
    plugin: community.digitalocean.digitalocean
    api_token: "{{ lookup('env', 'DO_API_TOKEN') }}"
    attributes:
      - id
      - name
      - networks
      - region
      - tags
    keyed_groups:
      - key: do_region.slug
        prefix: region
      - key: do_tags | default(['untagged'], true)
        prefix: tag

    Other providers

    Similar plugins exist for Azure, GCP, Hetzner, Linode, Vultr, Proxmox, VMware, and more. Search the Ansible collection index.

    Ansible AWX and Semaphore

    Running playbooks from a laptop is fine for one person. Teams need a UI, RBAC, scheduled runs, and audit logs. Two popular options:

    • Ansible AWX (github.com/ansible/awx) — the open-source upstream of Red Hat Ansible Automation Platform. Kubernetes-native, full-featured, heavier to operate.
    • Semaphore (semaphoreui.com) — lightweight Go-based alternative. Runs as a single binary or Docker container. Much easier to deploy on a VPS.
    Semaphore in particular pairs well with a CloudCore Starter VPS: install Docker, run docker compose up -d, and you have a team-friendly Ansible UI in about ten minutes.

    Best Practices

    After a few years of running Ansible at scale, these are the habits that pay off:

  • Keep everything in Git. Playbooks, roles, inventories, and (encrypted) Vault files. Pull-request workflows for infrastructure changes.
  • Use project-local config. Put ansible.cfg, inventory/, roles/, group_vars/, host_vars/, and requirements.yml in the repo. Avoid depending on /etc/ansible/.
  • Pin versions. Ansible core, collections, roles — all pinned in requirements.yml. No surprise upgrades.
  • Name every task. - name: Install Nginx reads better in logs than the bare module call.
  • Prefer modules over command/shell. Modules are idempotent; shell commands often aren't.
  • Use --check and --diff before prod runs. Ansible's dry run is genuinely useful.
  • Tag tasks and plays. ansible-playbook site.yml --tags "nginx,ssl" is faster than re-running everything.
  • Separate secrets from config. vault.yml for encrypted, vars.yml for plaintext aliases.
  • Test with Molecule. The Molecule framework spins up containers to test roles in isolation.
  • Set forks higher than the default 5. In ansible.cfg: forks = 50 for fleets of dozens of servers.
  • Example project-local ansible.cfg:

    ini
    [defaults]
    inventory = ./inventory
    roles_path = ./roles
    collections_paths = ./collections
    host_key_checking = False
    retry_files_enabled = False
    forks = 50
    stdout_callback = yaml
    callbacks_enabled = timer, profile_tasks

    [ssh_connection] pipelining = True control_path = ~/.ssh/ansible-%%r@%%h:%%p

    Troubleshooting

    SSH connection errors

    text
    UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ..."}
    • Confirm you can SSH manually: ssh deploy@host.
    • Check ansible_user, ansible_port, ansible_host in inventory.
    • Disable host key checking temporarily: export ANSIBLE_HOST_KEY_CHECKING=False.
    • Increase verbosity: ansible-playbook site.yml -vvvv shows the raw SSH command.

    Python interpreter errors

    text
    /usr/bin/python: not found

    Ubuntu 24.04 only ships Python 3. Tell Ansible explicitly:

    ini
    [all:vars]
    ansible_python_interpreter=/usr/bin/python3

    Permission denied (sudo)

    text
    sudo: a password is required

    Either set up passwordless sudo (see SSH Key Setup) or run with --ask-become-pass / -K.

    Slow runs

    • Enable SSH pipelining (see ansible.cfg above) — often a 3-5x speedup.
    • Use async and poll: 0 for long-running tasks that don't block the next step.
    • Tune forks to match your fleet size.
    • Use strategy: free to let hosts progress independently instead of waiting on the slowest.

    YAML syntax errors

    YAML is whitespace-sensitive. Use yamllint and ansible-lint:

    bash
    pip install yamllint ansible-lint
    ansible-lint site.yml

    FAQ

    Ansible vs. Puppet/Chef — which should I choose?

    For most new projects, Ansible. It has a shallower learning curve (YAML beats Ruby DSLs), no agent to manage, and a faster feedback loop. Puppet and Chef are still strong in large, legacy enterprises where pull-based architectures (agents checking into a central server) are already embedded. Pick Puppet/Chef if your team already runs them; pick Ansible if you're starting fresh.

    Ansible vs. Terraform — aren't they the same thing?

    No. Terraform provisions infrastructure (create a VPS, a database, a load balancer). Ansible configures infrastructure (install packages, write config files, start services). They're complementary:

    • Terraform creates the VPS.
    • Ansible installs and configures software on it.
    Many teams use both: Terraform for cloud resources, Ansible for OS-level configuration. Ansible can provision cloud resources, but Terraform's state management and plan/apply model are better for that job.

    Do I need Ansible for just one server?

    Probably not for a single static server — plain shell scripts are fine. But the moment you have two (staging + prod, or two web nodes), Ansible pays off. You get repeatability, documentation-as-code, and the ability to rebuild a server from scratch in minutes.

    Is Ansible slow?

    Ad-hoc, it can be. With pipelining = True, forks tuned up, fact caching enabled, and the mitogen strategy plugin, a well-configured Ansible run is plenty fast for fleets into the hundreds. For thousands of nodes, consider AWX with execution-environment containers and distributed workers.

    What Python version does Ansible need on targets?

    Ansible-core 2.17 requires Python 3.7+ on targets. Ubuntu 24.04 ships Python 3.12 by default, so you're good. For older targets (CentOS 7, Ubuntu 18.04), you may need to install Python explicitly or use the raw module to bootstrap.

    Can Ansible manage Windows?

    Yes. Ansible connects over WinRM (or SSH on modern Windows) and ships a full suite of win_* modules (win_package, win_service, win_regedit, etc.). The control node must still be Linux or macOS — Windows is supported as a target, not a controller.

    Next Steps

    You now have Ansible installed, your first inventory, a working playbook, and a map of the production patterns. From here:

  • Convert an existing bash provisioning script into an Ansible role. This is the single fastest way to internalize the mental model.
  • Pick one Galaxy role (geerlingguy.docker, geerlingguy.postgresql) and read the source — it's a masterclass in idempotent Ansible.
  • Set up Molecule to unit-test your roles in Docker.
  • Wire Ansible into CI. GitHub Actions or GitLab CI can run ansible-playbook --check on every PR and ansible-playbook on merge to main.
  • Graduate to AWX or Semaphore when more than one person needs to run playbooks.
  • Consider pairing Ansible with Terraform for full infrastructure-as-code.
  • Managing a fleet of VPS nodes? Spin up a CloudCore Starter plan as your Ansible control node and start codifying your ops today. Your future self — and your team — will thank you.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket