By now, my little homelab has grown into a small but mighty fleet of virtual machines β almost a dozen of them are humming along nicely. But with more services come more logsβ¦ and more chaos. π
To bring order to that chaos, I’m using the open-source log shipper Fluent Bit to collect logs from all my VMs and forward them to a centralized OpenSearch instance for analysis and visualization.
The goal? Collect everything β from disk space warnings, failed login attempts, and systemd noise to juicy kernel messages. All logs across all my machines should end up neatly indexed and searchable in a single OpenSearch dashboard.
(I’ll walk you through the OpenSearch setup in a separate post soon.)
Now, instead of manually logging into every VM and configuring Fluent Bit by hand (no thanks π), Iβve built a small, tidy Ansible Playbook that automates the entire setup.
Hereβs what the playbook does:
- Installs Fluent Bit
- Uploads a custom config file from the local
files/
directory - Ensures the service is started and enabled
Once the playbook is ready, I can deploy Fluent Bit to all VMs at once, in parallel β no manual work required. Just one command, and Ansible takes care of the rest.
One of the best things about Ansible is its idempotence: it smartly checks the state of each target system and only runs the tasks that are actually needed. You can run the playbook multiple times without breaking things β and thatβs pure DevOps magic. β¨
To make this work, thereβs one more file involved: a hosts.ini
inventory file, which lists the IP addresses of all my VMs and specifies how Ansible should SSH into them.
--- # Ansible Playbook to install Fluent Bit
- name: Install packages and configure Fluent Bit on Debian 12
hosts: homelab
become: true
vars:
fluentbit_custom_config: true # Set to true to upload your own config file from "files/"
tasks:
- name: Ensure gpg is installed
ansible.builtin.apt:
name: gnupg
state: present
update_cache: yes
- name: Import Fluent Bit GPG key
ansible.builtin.apt_key:
url: https://packages.fluentbit.io/fluentbit.key
keyring: /usr/share/keyrings/fluentbit-archive-keyring.gpg
state: present
- name: Add Fluent Bit repository
ansible.builtin.apt_repository:
repo: deb [signed-by=/usr/share/keyrings/fluentbit-archive-keyring.gpg] https://packages.fluentbit.io/debian/bookworm bookworm main
filename: fluentbit
state: present
- name: Update apt cache
ansible.builtin.apt:
update_cache: yes
- name: Install Fluent Bit
ansible.builtin.apt:
name: fluent-bit
state: present
- name: Optional - Deploy custom fluent-bit.conf from files/
ansible.builtin.copy:
src: fluent-bit.conf
dest: /etc/fluent-bit/fluent-bit.conf
owner: root
group: root
mode: '0644'
notify: Restart fluent-bit
when: fluentbit_custom_config | default(false)
- name: Enable and start Fluent Bit service
ansible.builtin.systemd:
name: fluent-bit
enabled: yes
state: started
handlers:
- name: Restart fluent-bit
ansible.builtin.systemd:
name: fluent-bit
state: restarted
Hereβs a sample output after running the playbook. As you can see, everything worked smoothly β except for pihole
, where gpg
was missing at first, causing the initial install to partially fail. After installing gpg
, the playbook ran through just fine:
root@seedVM:~/ansible/fluent-bit# ansible-playbook install_and_configure.yml -i hosts.ini
PLAY [Install packages and configure Fluent Bit on Debian 12] ************************************************
TASK [Gathering Facts] ***************************************************************************************
ok: [pihole]
ok: [gitlab]
...
TASK [Install Fluent Bit] ************************************************************************************
ok: [gitlab]
ok: [grafana]
changed: [pihole]
...
TASK [Enable and start Fluent Bit service] *******************************************************************
ok: [nextcloud]
changed: [pihole]
...
RUNNING HANDLER [Restart fluent-bit] *************************************************************************
changed: [pihole]
PLAY RECAP ****************************************************************************************************
blog : ok=8 changed=0 unreachable=0 failed=0
pihole : ok=9 changed=7 unreachable=0 failed=0
seedVM : ok=8 changed=0 unreachable=0 failed=0