Plays for your server on Raspberry Pi using Ansible
How I went from manually configuring Raspberry PI

Picture this: It's 2 AM, and I'm hunched over my laptop, SSH'd into my third Raspberry Pi of the week, manually installing packages and copying files for what feels like the hundredth time. Sound familiar? If you've ever found yourself in this soul-crushing cycle of repetitive server configuration, then buckle up – because I'm about to share how Ansible transformed my Raspberry Pi projects from a tedious chore into an elegant, automated symphony.
The Lightbulb Moment: Why Ansible?
My breaking point came when I realized I had been setting up the same Apache server configuration across multiple servers for a distributed web project. Each time, I'd SSH in, run the same commands, copy the same files, and inevitably forget some crucial step that would leave me debugging for hours. There had to be a better way.
Setting the Foundation: Installing Ansible

The journey began with the deceptively simple task of installing Ansible on my control machine (my trusty laptop). A quick sudo apt install ansible In my Debian computer, I was ready to dive into the world of infrastructure as code.
The SSH Dance: Keys, Trust, and Remote Access
Before Ansible could work its magic, I needed to establish secure, passwordless connections to my Raspberry Pis. This meant diving into the world of SSH key pairs. Then I copied the key to the raspebryy Pi


Testing the SSH key pair to see if it is working, and I can use it to access the Raspberry Pi server

First Contact: The Ansible Ping
With SSH keys in place, it was time for the moment of truth. I created my first inventory.ini file – a simple file that would become the phone book for my entire infrastructure:
[raspberrypi]
192.168.1.71 ansible_user=muthuri ansible_ssh_private_key_file=./server_key
#you can add more servers here
Then came the magical first command: ansible -i inventory.ini -m ping all. Watching those green "SUCCESS" messages appear for each host was like seeing my infrastructure come alive. The ping module, one of Ansible's simplest yet most essential modules, confirmed that Ansible could reach each machine and execute basic operations.

This wasn't just a network ping – it was Ansible's way of saying "I can connect, I can authenticate, and I'm ready to work." That simple test gave me the confidence to move forward with more complex automation.
From Commands to Code: My First Playbook
Ad hoc commands are great for quick tasks, and I spent considerable time exploring them. Commands like ansible -i inventory.ini -m shell -a "sudo ls /etc/" all Let me peek into the file systems of all my Raspberry Pis simultaneously.

But ad hoc commands, while powerful, felt ephemeral. I needed something more permanent, more maintainable. Enter playbooks – Ansible's way of turning infrastructure management into declarative poetry.
My first playbook was modest but meaningful. I wanted to set up Apache2 web servers across my Raspberry Pi cluster and deploy a simple web application:
---
- name: Install Apache2 and deploy application
hosts: all
remote_user: muthuri
become: yes
tasks:
- name: Install Apache2
ansible.builtin.apt:
name: apache2
state: present
update_cache: yes
- name: Copy html files
ansible.builtin.copy:
src: index.html
dest: /var/www/index.html
owner: muthuri
group: muthuri
mode: "0644"
The Power of Modules: Ansible's Swiss Army Knife
What struck me most about writing this playbook was the elegance of Ansible's modules. Each module is like a specialized tool designed for a specific task. The ansible.builtin.apt module doesn't just run apt install – it's intelligent enough to handle package states, cache updates, and dependency resolution. The ansible.builtin.copy module isn't just scp – it preserves ownership, sets permissions, and can even template files on the fly.
Running the playbook with ansible-playbook -i inventory.ini playbook.yaml --ask-become-pass felt like conducting an orchestra. I could watch as each task executed across multiple machines simultaneously, transforming raw Ubuntu Server installations into configured web servers in minutes rather than hours.

Standing on the Shoulders: Ansible Galaxy
As my automation needs grew more complex, I discovered Ansible Galaxy – the community-driven repository of pre-built roles. Why reinvent the wheel when thousands of DevOps engineers have already solved the same problems?
I needed Docker on my Raspberry Pis for a containerized project, and rather than writing dozens of tasks to handle Docker installation, configuration, and service management, I found bsmeding.docker on Galaxy. A simple ansible-galaxy role install bsmeding.docker downloaded a professionally-crafted role that handled all the Docker setup complexities I hadn't even thought of.
Applying this role was beautifully simple:
---
- hosts: all
become: true
roles:
- bsmeding.docker
This tiny playbook accomplished what would have taken me dozens of manual commands and error-prone configurations. The role handled Docker repository setup, package installation, service configuration, and user permissions – all with the reliability that comes from being tested across thousands of deployments.
The Great Refactoring: When Playbooks Grow Up
Success breeds complexity, and my single playbook file was becoming unwieldy. What started as a simple Apache setup had grown into a monolithic YAML monster handling web servers, databases, monitoring, and application deployment. It was time for some serious refactoring.
This is where ansible-galaxy init <role name> became my lifesaver. This command creates a proper role structure that transforms chaotic playbooks into organized, maintainable infrastructure code:
./<role name>/
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
Organizing the Chaos: Role Structure Deep Dive
Each directory in this structure serves a specific purpose, and understanding them transformed how I thought about infrastructure automation:
Tasks became my main workflow, broken down into logical, reusable chunks. Instead of one massive task list, I could organize related operations and even include tasks conditionally.
Templates introduced me to the power of Jinja2 templating. Instead of static configuration files, I could generate dynamic configs based on host variables, group membership, or gathered facts. My Apache virtual host configurations could now adapt automatically to each Raspberry Pi's hostname and IP address.
Handlers revolutionized how I dealt with service management. Instead of restarting Apache after every configuration change, handlers let me queue up service restarts and execute them only when necessary, and only once per playbook run.
Defaults and Vars gave me the flexibility to customize role behavior without modifying the role itself. I could set sensible defaults while allowing easy overrides for different environments or use cases.
Error Handling: When Things Go Wrong (And They Will)
Real-world infrastructure is messy, and not every task will succeed every time. Network hiccups, package repository issues, or filesystem permissions can derail even the most carefully crafted playbooks. This is where Ansible's error-handling capabilities saved my sanity.
The ignore_errors: yes directive became my strategic tool for handling non-critical failures. When deploying to a mixed environment of Raspberry Pi models with different capabilities, some tasks might fail on older hardware but succeed on newer units. Rather than letting one failure halt the entire deployment, I could acknowledge that some operations might fail while allowing the rest of the configuration to proceed.
But ignoring errors isn't always the right approach. Sometimes you need to handle failures gracefully, provide fallbacks, or gather information about why something failed. Ansible's failed_when, changed_when, and register Keywords opened up sophisticated error handling patterns that made my playbooks robust enough for production use.
Lessons from the Trenches
This journey taught me that automation isn't just about saving time – it's about reducing cognitive load, eliminating human error, and creating reproducible systems. Every manual task I automated was one less thing I had to remember, one less opportunity for mistakes, and one step closer to infrastructure that could heal itself.
The modular nature of Ansible roles meant I could share my Raspberry Pi configurations with friends, contribute to the community, and build upon the work of others. My infrastructure became not just automated, but collaborative.
The Road Ahead: Beyond Basic Automation
As I write this, my Ansible-managed Raspberry Pi cluster is running web applications, monitoring systems, and development environments with minimal manual intervention. What started as a frustration with repetitive tasks has become a foundation for sophisticated home lab infrastructure.
The real revelation wasn't just that Ansible could automate my tasks – it was that thinking in terms of desired state rather than procedural steps fundamentally changed how I approached infrastructure problems. Instead of asking "what commands do I need to run?" I started asking, "What should the end state look like?"
Whether you're managing two Raspberry Pis or two hundred servers, the principles remain the same: define your desired state, let Ansible figure out how to get there, and focus your energy on solving problems rather than repeating solutions.
The next time you find yourself SSH'd into a server at 2 AM, manually configuring the same service you've set up countless times before, remember that there's a better way. Ansible is waiting to transform your infrastructure chaos into controlled, elegant automation.
What's Next: Giving Back to the Community
Now that I've mastered the art of Ansible automation with Raspberry Pis, my next goal is to package my most useful roles and share them with the broader community. I'm planning to create specialized roles for common Raspberry Pi scenarios – maybe a comprehensive IoT sensor setup role, or a complete home lab monitoring stack – and publish them to Ansible Galaxy.



