Complete guide for adding new self-hosted applications to the home-server Ansible infrastructure. Use this skill when the user wants to add a new service, create a new role, or deploy a new self-hosted application. Covers role structure, integration patterns (firewall, NGINX, SELinux, DNS), installation methods (binary, package, container), and testing procedures.
This skill provides comprehensive guidance for adding new self-hosted applications to the home-server Ansible infrastructure. It documents all established patterns, conventions, and integration requirements to ensure consistent, secure, and maintainable role implementations.
Activate this skill when:
This skill includes detailed reference files for in-depth information:
references/role-examples.md - Complete real-world examples:
- Comprehensive checklists:
references/checklists.mdLoad these reference files when detailed examples or comprehensive checklists are needed.
Follow this workflow for every new service:
Determine Installation Method:
Is the service containerized?
├─ Yes → Use Podman Quadlet pattern (see references/role-examples.md: Immich)
└─ No → Is it available in DNF/RPM repositories?
├─ Yes → Use Package installation (see references/role-examples.md: Jellyfin)
└─ No → Use Binary download/installation (see references/role-examples.md: FileBrowser)
Identify Required Integrations:
Create the role directory structure:
mkdir -p roles/[service_name]/{defaults,tasks,handlers,templates,meta}
Required directories:
defaults/ - Default variables (always created)tasks/ - Task files (always created)handlers/ - Event handlers (always created)templates/ - Jinja2 templates (if service needs config files or systemd units)meta/ - Role metadata (always created)Define all configurable variables following this pattern:
---
# Default variables for [Service] role
# Service user configuration
service_user: ndelucca
service_group: ndelucca
# Directory configuration
service_base_dir: /opt/service # or /srv/service
service_working_dir: "{{ service_base_dir }}/data"
service_config_dir: "{{ service_base_dir }}/config"
# Service configuration
service_name: service
service_enabled: true
service_state: started
# Network configuration
service_bind_address: 127.0.0.1 # ALWAYS 127.0.0.1 for web services
service_port: 8080
# Firewall settings
service_firewall_enabled: false # false if behind NGINX
service_firewall_zone: FedoraServer
# SELinux configuration
service_manage_selinux: true
See references/checklists.md for complete variable definition checklist.
Orchestration file that imports modular task files:
---
# Main entry point for [Service] role
- name: Include preflight checks
ansible.builtin.import_tasks: preflight.yml
tags: ['service', 'preflight']
- name: Install [Service]
ansible.builtin.import_tasks: install.yml
tags: ['service', 'install']
- name: Configure [Service] application
ansible.builtin.import_tasks: configure.yml
tags: ['service', 'configure']
when: service_use_config_file | bool
- name: Configure systemd service
ansible.builtin.import_tasks: service.yml
tags: ['service', 'systemd']
- name: Configure SELinux
ansible.builtin.import_tasks: selinux.yml
tags: ['service', 'selinux']
when: service_manage_selinux | bool
Create these task files based on service type:
Always Required:
preflight.yml - OS verification, directory creationinstall.yml - Service installation (method varies by type)service.yml - Systemd service managementselinux.yml - SELinux contexts and portsConditional:
configure.yml - If service needs configuration filesrepository.yml - If package needs external repositoryquadlet.yml - If using Podman containersFor detailed implementation examples, see references/role-examples.md.
Standard Services:
---
# Handlers for [Service] role
- name: daemon-reload
ansible.builtin.systemd:
daemon_reload: true
become: true
- name: restart service
ansible.builtin.systemd:
name: "{{ service_name }}"
state: restarted
become: true
- name: apply selinux context
ansible.builtin.command: "restorecon -Rv {{ item }}"
become: true
loop:
- "{{ service_install_dir }}/service"
- "{{ service_working_dir }}"
changed_when: false
Rootless Podman Services:
---
# Handlers for rootless Podman service
- name: daemon-reload-user
ansible.builtin.systemd:
daemon_reload: true
scope: user
become: true
become_user: "{{ service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ service_uid }}"
- name: restart service-pod
ansible.builtin.systemd:
name: "{{ service_name }}"
state: restarted
scope: user
become: true
become_user: "{{ service_user }}"
environment:
XDG_RUNTIME_DIR: "/run/user/{{ service_uid }}"
---
galaxy_info:
author: Naza
description: Install and configure [Service] on Fedora
license: MIT
min_ansible_version: '2.13'
platforms:
- name: Fedora
versions:
- all