Ansible - Technology Guide¶
This guide explains what Ansible is, how it works, and how it is used to automate node setup and application deployment in this homelab. No prior Ansible experience required.
What is Ansible?¶
Ansible is an open-source configuration management and automation tool. It allows you to define what you want a server to look like in simple YAML files (called playbooks), and Ansible will make it so.
What makes Ansible different from other tools:
- Agentless - no software needs to be installed on the target servers
- Uses SSH - connects via standard SSH (or Tailscale SSH in this homelab)
- Idempotent - running the same playbook multiple times has the same result (safe to re-run)
- Declarative - you describe the desired state, not the steps to get there
Without Ansible:
# Manually SSH to every node and run:
ssh ubuntu@k3s-server.tailnet.ts.net
curl -sfL https://get.k3s.io | sh - # What flags? I forgot...
# Then repeat for k3s-agent-1, k3s-agent-2
# What if one node is already configured and the other isn't?
With Ansible:
References:
Key Concepts¶
Inventory¶
An inventory is a file that lists the servers Ansible will manage. For this homelab, GitHub Actions workflows generate the inventory dynamically from workflow inputs:
# Example inventory generated by the k3s workflow
all:
children:
k3s:
hosts:
k3s-server:
ansible_host: k3s-server.tailnet.ts.net
ansible_user: ubuntu
ansible_ssh_common_args: "-o StrictHostKeyChecking=yes"
vars:
ansible_python_interpreter: /usr/bin/python3
Playbook¶
A playbook is a YAML file that describes automation tasks. Each playbook:
- Targets specific hosts (from the inventory)
- Runs a list of tasks in order
- Can include variables, conditionals, loops, and error handling
# Example structure of a playbook
- name: Deploy k3s server
hosts: k3s # Which hosts to target
become: true # Run as root (sudo)
pre_tasks:
- name: Update apt cache
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600 # Don't update if done in the last hour
tasks:
- name: Install k3s
ansible.builtin.shell:
cmd: "curl -sfL https://get.k3s.io | sh -s - server"
Module¶
An Ansible module is a reusable unit of automation. Modules handle specific tasks:
| Module | What It Does |
|---|---|
ansible.builtin.apt |
Install/remove packages on Debian/Ubuntu |
ansible.builtin.shell |
Run shell commands |
ansible.builtin.copy |
Copy files to servers |
ansible.builtin.template |
Copy Jinja2 template files |
ansible.builtin.service |
Start/stop/enable systemd services |
ansible.builtin.file |
Create/delete files and directories |
ansible.builtin.get_url |
Download files from URLs |
ansible.builtin.assert |
Validate conditions (fail with message if not met) |
FQCN (Fully Qualified Collection Name)¶
This homelab uses the full module names like ansible.builtin.apt instead of just apt.
This is best practice as it avoids ambiguity and works correctly with all Ansible versions.
Idempotency¶
An idempotent operation produces the same result whether run once or many times. Most Ansible modules are idempotent:
ansible.builtin.apt: name=curl state=present- installs curl if not installed, does nothing if already installedansible.builtin.service: name=k3s state=started- starts k3s if not running, does nothing if already running
Ansible Playbooks in This Homelab¶
All playbooks are in ansible/playbooks/.
Running Ansible Manually¶
While GitHub Actions is the recommended path, you can run playbooks manually:
# Install Ansible
pip install ansible
# Ensure you're on the tailnet
tailscale up
# Create a minimal inventory file
cat > /tmp/inventory.yml <<EOF
all:
children:
k3s:
hosts:
k3s-server:
ansible_host: k3s-server.tailnet.ts.net
ansible_user: ubuntu
EOF
# Dry-run (check mode - no changes made)
ansible-playbook playbooks/deploy_k3s.yml \
-i /tmp/inventory.yml \
--check
# Actually run it
ansible-playbook playbooks/deploy_k3s.yml \
-i /tmp/inventory.yml
# Run with increased verbosity for debugging
ansible-playbook playbooks/deploy_k3s.yml \
-i /tmp/inventory.yml \
-v # -v, -vv, or -vvv for increasing detail
# Run only specific tasks (by tags, if defined)
ansible-playbook playbooks/deploy_k3s.yml \
-i /tmp/inventory.yml \
--tags "install"
Common Troubleshooting¶
SSH connection refused¶
# Check Tailscale is running on the target node
tailscale ping k3s-server
# Check if Tailscale SSH is enabled
tailscale status | grep k3s-server
Task fails with "Permission denied"¶
The task needs become: true (sudo). Check if the playbook or task has become: true.
Playbook fails but the server is in a weird state¶
Most Ansible modules are idempotent - you can re-run the playbook and it will pick up where it left off without breaking things that already succeeded.
"No module named 'ansible'"¶
Ansible is not installed:
Known hosts verification fails¶
If a VM was recreated (new host key), remove the old entry:
Or repopulate the host key with ssh-keyscan: