Ansible Setup and Best Practices Guide #
This document provides a comprehensive guide to setting up and using Ansible for managing Ubuntu servers, based on a real-world learning process. It covers everything from installing Ansible to structuring projects with roles, managing different environments, and troubleshooting common errors. I had gemini pro create this document below after the back and forth I had while setting up and getting into Ansible. It is pretty great if you have a model with lot’s of context as you can actually instruct it to create markdown files based on the interactions and learnings you had.
flowchart LR
A["Control Server"]
A --> B["Target Server 1"]
A --> C["Target Server 2"]
How I now use it is that I create the VM’s that I want to control via ansible by hand in Proxmox. I know I can automate that too, but I do not do that enough (at this moment) to automate that part too (maybe in the future). Currently I quickly create a Ubuntu server VM on my proxmox cluster, run through the setup process, and use the steps in this document to enable control by ansible. Then running ansible makes the servers into what I need them to be, installing basic tools, docker etc. On my pages on Hugo and Gitlab, I also used it to setup those tools. It makes installations repeatable and controlled. And the neat thing is that you can actually use gemini as well to help you write the ansible playbooks!
of course there are also some awesome websites with good instructions
1. Setting Up the Ansible Control Node #
The control node is the machine where Ansible is installed and from which you will manage your other servers.
1.1. Install Ansible on an Ubuntu Server: First, update your system and install the necessary prerequisites, then add the Ansible PPA (Personal Package Archive) to get the latest version.
# Update package list and install prerequisites
sudo apt update
sudo apt install software-properties-common
# Add the official Ansible PPA
sudo add-apt-repository --yes --update ppa:ansible/ansible
# Install Ansible
sudo apt install ansible
1.2. Verify the Installation: Check that Ansible was installed correctly.
ansible --version
1.3. Create a Project Directory: It’s best practice to keep all your Ansible files organized in a dedicated project directory.
mkdir ~/ansible_project
cd ~/ansible_project
2. Onboarding a New Target Host #
A target host is any server you want to manage with Ansible. Before Ansible can connect, the host needs to be prepared for secure, passwordless communication.
2.1. Create a Management User:
Ensure your management user (e.g., my_super_user) exists on the target host.
2.2. Set Up SSH Key Authentication (from Control Node): This allows Ansible to log in without needing a password.
# Generate an SSH key on your control node if you don't have one
ssh-keygen -t rsa -b 4096
# Copy your public key to the target host
ssh-copy-id my_super_user@<target_server_ip>
2.3. Grant Passwordless sudo Privileges (on Target Host):
Ansible needs to perform administrative tasks. Granting passwordless sudo is essential for automation.
-
Command:
sudo visudo -f /etc/sudoers.d/90-my_super_user -
Content to add:
my_super_user ALL=(ALL) NOPASSWD: ALL -
Why
visudo? It’s a special editor that syntax-checks your changes before saving, preventing you from getting locked out ofsudo. -
Why
/etc/sudoers.d/? This directory keeps your custom rules separate from the main/etc/sudoersfile, so they won’t be overwritten by system updates. -
Why
90-? Files in this directory are read in alphabetical order. A high number like90ensures your rule is read last and takes precedence over any other conflicting rules.
3. Best Practice Project Structure with Roles #
As your project grows, using roles is the best way to keep it organized, reusable, and maintainable.
3.1. Create the Directory Structure:
Use the ansible-galaxy command to create a skeleton structure for your roles.
# From within your ansible_project directory
mkdir -p roles
cd roles
ansible-galaxy init common
ansible-galaxy init docker
3.2. Final Project Structure: Your project should look like this:
ansible_project/
├── hosts # Inventory file listing your servers
├── roles/
│ ├── common/ # For tasks common to all servers
│ │ └── tasks/
│ │ └── main.yml
│ └── docker/ # For tasks specific to setting up Docker
│ └── tasks/
│ └── main.yml
└── site.yml # The main playbook that calls the roles
4. The hosts Inventory File
#
The inventory file lists your servers and groups them into logical environments like testing and production.
File: ~/ansible_project/hosts
[testing]
thor ansible_host=192.168.66.66
[production]
artemis ansible_host=192.168.66.166
# A parent group that contains both testing and production
[docker_servers:children]
testing
production
[all:vars]
ansible_user=my_super_user
5. The common Role: Baseline Configuration
#
This role installs packages and configures services that you want on all your servers.
File: ~/ansible_project/roles/common/tasks/main.yml
---
# tasks file for common
- name: Update apt cache and install common prerequisites
ansible.builtin.apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
- qemu-guest-agent # For Proxmox integration
- btop # A useful monitoring tool
state: present
update_cache: yes
- name: Ensure the QEMU Guest Agent is running
ansible.builtin.service:
name: qemu-guest-agent
state: started
enabled: yes
6. The docker Role: Application-Specific Configuration
#
This role contains all the logic required to install and configure Docker.
File: ~/ansible_project/roles/docker/tasks/main.yml
---
# tasks file for docker
- name: Create directory for Docker's GPG key
ansible.builtin.file:
path: /etc/apt/keyrings
state: directory
mode: '0755'
- name: Download and dearmor Docker's official GPG key
ansible.builtin.shell:
cmd: "curl -fsSL [https://download.docker.com/linux/ubuntu/gpg](https://download.docker.com/linux/ubuntu/gpg) | gpg --dearmor -o /etc/apt/keyrings/docker.gpg"
creates: /etc/apt/keyrings/docker.gpg
- name: Add Docker repository source file
ansible.builtin.copy:
dest: /etc/apt/sources.list.d/docker.list
content: "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] [https://download.docker.com/linux/ubuntu](https://download.docker.com/linux/ubuntu) {{ ansible_lsb.codename }} stable"
owner: root
group: root
mode: '0644'
- name: Install or update Docker Engine and Docker Compose
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
state: latest # 'latest' ensures packages are updated on each run
- name: Add {{ ansible_user }} user to the docker group
ansible.builtin.user:
name: "{{ ansible_user }}"
groups: docker
append: yes
- name: Ensure Docker service is running and enabled on boot
ansible.builtin.service:
name: docker
state: started
enabled: yes
7. The Main Playbook (site.yml)
#
This top-level playbook is now very simple. It just specifies which hosts to target and which roles to apply.
File: ~/ansible_project/site.yml
---
- name: Configure Docker Servers
hosts: docker_servers
become: true
roles:
- common
- docker
8. Running Playbooks Safely #
Always test your changes before applying them to production.
- Check Mode (Dry Run): See what would change without making any modifications.
ansible-playbook -i hosts site.yml --check - Check Mode with Diff: See the exact file changes that would occur.
ansible-playbook -i hosts site.yml --check --diff - Limit to a Test Server: Run the playbook only against your
testinggroup.ansible-playbook -i hosts site.yml --limit testing - Run on Production: Once verified, run against the
productiongroup.ansible-playbook -i hosts site.yml --limit production
9. Troubleshooting Common Errors #
-
GPG Key Error (
NO_PUBKEY): This error occurs whenaptdoesn’t trust the Docker repository.- Cause: The GPG key was not downloaded or was in the wrong format.
- Solution: Use the
shellmodule withgpg --dearmorto download and convert the key to the required binary format (as shown in thedockerrole).
-
Errors on Reruns: An error occurs on the very first task (
Update apt cache).- Cause: A file from a previous failed run (e.g.,
/etc/apt/sources.list.d/docker.list) was left on the target host, causingaptto fail before Ansible can fix the problem. - Solution: Manually SSH into the target host and remove the leftover configuration files before rerunning the playbook.
sudo rm /etc/apt/sources.list.d/docker.list sudo rm /etc/apt/keyrings/docker.gpg
- Cause: A file from a previous failed run (e.g.,