/
/
/
Ansible role that provisions my storage server.
1---
2# Storage Services - Arr Stack (Sonarr, Radarr, Prowlarr, LazyLibrarian + Gluetun VPN)
3
4- name: Create Arr Stack configuration directory structure
5 file:
6 path: "{{ item }}"
7 state: directory
8 owner: "{{ storage_user }}"
9 group: "{{ storage_group }}"
10 mode: '0775'
11 loop:
12 - "{{ arr_config_dir }}"
13 - "{{ sonarr_config_dir }}"
14 - "{{ radarr_config_dir }}"
15 - "{{ prowlarr_config_dir }}"
16 - "{{ lazylibrarian_config_dir }}"
17 - "{{ bazarr_config_dir }}"
18 - "{{ jellyseer_config_dir }}"
19 - "{{ gluetun_config_dir }}"
20 - "{{ flaresolverr_config_dir }}"
21 - "{{ qbittorrent_config_dir }}"
22 - "{{ lidarr_config_dir }}"
23
24- name: Set group sticky bit on Arr Stack directories for permission inheritance
25 file:
26 path: "{{ item }}"
27 state: directory
28 mode: "g+s"
29 loop:
30 - "{{ arr_config_dir }}"
31 - "{{ sonarr_config_dir }}"
32 - "{{ radarr_config_dir }}"
33 - "{{ prowlarr_config_dir }}"
34 - "{{ lazylibrarian_config_dir }}"
35 - "{{ bazarr_config_dir }}"
36 - "{{ jellyseer_config_dir }}"
37 - "{{ flaresolverr_config_dir }}"
38 - "{{ qbittorrent_config_dir }}"
39 - "{{ lidarr_config_dir }}"
40
41- name: Deploy LazyLibrarian custom Dockerfile
42 copy:
43 src: lazylibrarian/Dockerfile
44 dest: "{{ arr_config_dir }}/Dockerfile"
45 owner: "{{ storage_user }}"
46 group: "{{ storage_group }}"
47 mode: '0644'
48 when: lazylibrarian_enabled and lazylibrarian_custom_build | default(false)
49 notify: restart arr-stack
50
51- name: Generate Arr Stack environment file
52 template:
53 src: arr-stack.env.j2
54 dest: "{{ arr_config_dir }}/.env"
55 owner: "{{ storage_user }}"
56 group: "{{ storage_group }}"
57 mode: '0660'
58 notify: restart arr-stack
59
60- name: Deploy Arr Stack Docker Compose file
61 template:
62 src: arr-stack-compose.yml.j2
63 dest: "{{ arr_config_dir }}/docker-compose.yml"
64 owner: "{{ storage_user }}"
65 group: "{{ storage_group }}"
66 mode: '0664'
67 notify: restart arr-stack
68
69- name: Check if Arr Stack directory exists
70 stat:
71 path: "{{ arr_config_dir }}"
72 register: arr_dir_stat
73 changed_when: false
74
75- name: Start Arr Stack services
76 community.docker.docker_compose_v2:
77 project_src: "{{ arr_config_dir }}"
78 state: present
79 register: arr_start_result
80 check_mode: no
81 when: arr_dir_stat.stat.exists
82
83- name: Wait for VPN connection if enabled
84 pause:
85 seconds: 30
86 when: gluetun_enabled
87
88- name: Verify Arr Stack services are running
89 uri:
90 url: "http://localhost:{{ item.port }}"
91 method: GET
92 status_code: [200, 302, 401]
93 register: arr_health
94 retries: 10
95 delay: 5
96 until: arr_health.status in [200, 302, 401]
97 ignore_errors: true
98 loop:
99 - { name: "Sonarr", port: "{{ sonarr_host_port }}" }
100 - { name: "Radarr", port: "{{ radarr_host_port }}" }
101 - { name: "Prowlarr", port: "{{ prowlarr_host_port }}" }
102 - { name: "LazyLibrarian", port: "{{ lazylibrarian_host_port }}" }
103 - { name: "Bazarr", port: "{{ bazarr_host_port }}" }
104 - { name: "Jellyseer", port: "{{ jellyseer_host_port }}" }
105 - { name: "Flaresolverr", port: "{{ flaresolverr_host_port }}" }
106 - { name: "qBittorrent", port: "{{ qbittorrent_host_port }}" }
107 - { name: "Lidarr", port: "{{ lidarr_host_port }}" }
108 when: arr_stack_enabled
109
110- name: Display Arr Stack deployment summary
111 debug:
112 msg: |
113 Arr Stack Deployment:
114 - Status: {{ 'Started' if arr_start_result is changed else 'Already running' }}
115 - VPN: {{ 'Enabled via Gluetun' if gluetun_enabled else 'Disabled' }}
116
117 Services:
118 {% if sonarr_enabled %}
119 - Sonarr (TV Shows): http://{{ ansible_default_ipv4.address }}:{{ sonarr_host_port }}
120 {% endif %}
121 {% if radarr_enabled %}
122 - Radarr (Movies): http://{{ ansible_default_ipv4.address }}:{{ radarr_host_port }}
123 {% endif %}
124 {% if prowlarr_enabled %}
125 - Prowlarr (Indexers): http://{{ ansible_default_ipv4.address }}:{{ prowlarr_host_port }}
126 {% endif %}
127 {% if lazylibrarian_enabled %}
128 - LazyLibrarian (Books): http://{{ ansible_default_ipv4.address }}:{{ lazylibrarian_host_port }}
129 {% endif %}
130 {% if bazarr_enabled %}
131 - Bazarr (Subtitles): http://{{ ansible_default_ipv4.address }}:{{ bazarr_host_port }}
132 {% endif %}
133 {% if jellyseer_enabled %}
134 - Jellyseer (Requests): http://{{ ansible_default_ipv4.address }}:{{ jellyseer_host_port }}
135 {% endif %}
136 {% if flaresolverr_enabled %}
137 - Flaresolverr (Cloudflare): http://{{ ansible_default_ipv4.address }}:{{ flaresolverr_host_port }}
138 {% endif %}
139 {% if qbittorrent_enabled %}
140 - qBittorrent (Downloads): http://{{ ansible_default_ipv4.address }}:{{ qbittorrent_host_port }}
141 {% endif %}
142 {% if lidarr_enabled %}
143 - Lidarr (Music): http://{{ ansible_default_ipv4.address }}:{{ lidarr_host_port }}
144 {% endif %}
145
146 Directories:
147 - Configuration: {{ arr_config_dir }}
148 - Downloads: {{ arr_downloads_dir }}
149
150 Next Steps:
151 1. Configure VPN credentials in encrypted vault
152 2. Set up indexers in Prowlarr
153 3. Configure quality profiles in each Arr app
154 4. Add root folders for media types
155 5. Configure download client settings
156 6. Set up Jellyseer with Jellyfin integration
157