Install the Latest Ansible on Ubuntu 24.04 with apt, Bootstrap Remotes, and Run a Minimal Project

Scope: apt-install Ansible, create an ansible user on the controller, provision remotes, and run a small project.


  1. Install Ansible and create the controller user
# as administrator
sudo apt update
sudo apt install software-properties-common
sudo add-apt-repository --yes --update ppa:ansible/ansible
sudo apt install ansible

# create the local ops user that will run playbooks
sudo adduser --gecos "" ansible

  1. Generate the controller SSH key (run as ansible)
sudo su - ansible
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519
mkdir -p ~/.ansible/facts   # for fact cache path used below

  1. Bootstrap each remote (run as controller ansible)

Replace jenkins.maksonlee.com as needed and repeat per host.

SERVER=jenkins.maksonlee.com  # or your target host

ssh -tt administrator@"$SERVER" 'sudo bash -c "
  set -euo pipefail
  id -u ansible >/dev/null 2>&1 || adduser --disabled-password --gecos \"\" ansible
  passwd -l ansible || true
  printf \"%s\n\" \"ansible ALL=(ALL) NOPASSWD:ALL\" > /etc/sudoers.d/90-ansible
  chmod 440 /etc/sudoers.d/90-ansible
  visudo -cf /etc/sudoers.d/90-ansible >/dev/null
"'

scp ~/.ssh/id_ed25519.pub administrator@"$SERVER":/tmp/ansible.pub

ssh -tt administrator@"$SERVER" 'sudo bash -c "
  set -euo pipefail
  mkdir -p /home/ansible/.ssh
  chmod 700 /home/ansible/.ssh
  chown ansible:ansible /home/ansible/.ssh
  install -o ansible -g ansible -m 600 /tmp/ansible.pub /home/ansible/.ssh/authorized_keys
  rm -f /tmp/ansible.pub
"'

Smoke test:

ssh ansible@"$SERVER" 'whoami && sudo whoami && hostname'
# expect: ansible / root / <hostname>

  1. Use this project layout
ansible-playbooks/
├─ .ansible/
├─ .vscode/
│  └─ settings.json
├─ group_vars/
│  └─ .gitkeep
├─ host_vars/
│  └─ .gitkeep
├─ playbooks/
│  ├─ haproxy.yml
│  ├─ letsencrypt.yml
│  └─ test.yml
├─ roles/
│  └─ .gitkeep
├─ .gitignore
├─ ansible.cfg
└─ hosts.yml

ansible.cfg

[defaults]
inventory = ./hosts.yml
remote_user = ansible
private_key_file = ~/.ssh/id_ed25519
host_key_checking = False
interpreter_python = auto_silent
forks = 20
gathering = smart
fact_caching = jsonfile
fact_caching_connection = ~/.ansible/facts

[ssh_connection]
pipelining = True

Notes:

  • host_key_checking = False is fast but less strict; if you prefer verification, set it to True and pre-add host keys with ssh-keyscan.
  • Ensure the cache dir exists: mkdir -p ~/.ansible/facts.

hosts.yml

all:
  hosts:
    thingsboard.maksonlee.com: {}
    kafka.maksonlee.com: {}
    jenkins.maksonlee.com: {}
    spark.maksonlee.com: {}
    client.maksonlee.com: {}
    cdlee-miniu.maksonlee.com: {}

  children:
    haproxy:
      hosts:
        jenkins.maksonlee.com: {}
        thingsboard.maksonlee.com: {}
    letsencrypt:
      hosts:
        jenkins.maksonlee.com: {}
        thingsboard.maksonlee.com: {}

  1. Sample playbooks

playbooks/test.yml

- name: Connectivity + privilege check
  hosts: letsencrypt
  gather_facts: true
  tasks:
    - name: Ping
      ansible.builtin.ping:

    - name: Who am I?
      ansible.builtin.command: whoami
      changed_when: false
      register: who

    - ansible.builtin.debug:
        msg: "Remote user: {{ who.stdout }}"

    - name: Check sudo
      become: true
      ansible.builtin.command: whoami
      changed_when: false
      register: rootwho

    - ansible.builtin.debug:
        msg: "Sudo user: {{ rootwho.stdout }}"

  1. Run it (controller ansible user)
ansible@client:~/ansible-playbooks$ /usr/bin/ansible-playbook  /home/ansible/ansible-playbooks/playbooks/test.yml

PLAY [Connectivity + privilege check] **********************************************************************************************************************************

TASK [Gathering Facts] *************************************************************************************************************************************************
ok: [jenkins.maksonlee.com]
ok: [thingsboard.maksonlee.com]

TASK [Ping] ************************************************************************************************************************************************************
ok: [thingsboard.maksonlee.com]
ok: [jenkins.maksonlee.com]

TASK [Who am I?] *******************************************************************************************************************************************************
ok: [jenkins.maksonlee.com]
ok: [thingsboard.maksonlee.com]

TASK [ansible.builtin.debug] *******************************************************************************************************************************************
ok: [jenkins.maksonlee.com] => {
    "msg": "Remote user: ansible"
}
ok: [thingsboard.maksonlee.com] => {
    "msg": "Remote user: ansible"
}

TASK [Check sudo] ******************************************************************************************************************************************************
ok: [jenkins.maksonlee.com]
ok: [thingsboard.maksonlee.com]

TASK [ansible.builtin.debug] *******************************************************************************************************************************************
ok: [jenkins.maksonlee.com] => {
    "msg": "Sudo user: root"
}
ok: [thingsboard.maksonlee.com] => {
    "msg": "Sudo user: root"
}

PLAY RECAP *************************************************************************************************************************************************************
jenkins.maksonlee.com      : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
thingsboard.maksonlee.com  : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top