/
/
/
This repo is destined for my server automations and setup.
1---
2# Storage Services Playbook
3# Comprehensive deployment for media automation and backup services
4
5# PHASE 1: Setup NAS exports for storage services (if needed for other hosts)
6# Note: Storage server typically hosts its own storage, so this phase may be empty
7- name: Storage Server Local Setup
8 hosts: storage_servers
9 become: yes
10 gather_facts: yes
11
12 vars:
13 # Enable basic system monitoring (mdadm, smartd)
14 nas_raid_monitoring: true
15 nas_smartmontools_enabled: true
16
17 pre_tasks:
18 - name: Verify storage server local requirements
19 assert:
20 that:
21 - storage_base_path is defined
22 - storage_raid_arrays is defined
23 - ansible_default_ipv4.address is defined
24 fail_msg: "Storage server local requirements not met. Check host variables."
25
26 - name: Display storage local setup information
27 debug:
28 msg: |
29 Setting up local storage services on: {{ inventory_hostname }}
30 Storage Base: {{ storage_base_path }}
31 RAID Arrays: {{ storage_raid_arrays | length }}
32
33 Local Services to Enable:
34 - RAID Health Monitoring
35 - SMART Drive Monitoring
36 - Storage Path Validation
37
38 roles:
39 # Only run monitoring tasks from NAS role for local storage
40 - role: nas
41 tags: ['storage', 'raid']
42 vars:
43 nas_nfs_enabled: false
44 nas_network_bonding_enabled: false
45 nas_performance_tuning_enabled: false
46 nas_backup_integration: false
47 nas_enable_runner_exports: false
48 nas_raid_monitoring: true
49 nas_smartmontools_enabled: true
50
51 post_tasks:
52 - name: Verify local monitoring services are active
53 systemd:
54 name: "{{ item }}"
55 state: started
56 enabled: yes
57 loop:
58 - mdmonitor
59 - smartd
60 tags: ['verification']
61
62# PHASE 2: Deploy storage services
63- name: Storage Services Deployment
64 hosts: storage_servers
65 become: yes
66 gather_facts: yes
67
68 vars:
69 # Override defaults for storage-specific deployment
70 storage_enabled: true
71 backup_enabled: true
72
73 pre_tasks:
74 - name: Verify storage server requirements
75 assert:
76 that:
77 - storage_base_path is defined
78 - storage_docker_dir is defined
79 - ansible_default_ipv4.address is defined
80 - storage_user is defined
81 - storage_group is defined
82 - storage_raid_arrays is defined
83 fail_msg: "Storage server requirements not met. Check host variables."
84
85 - name: Validate storage paths exist and are accessible
86 block:
87 - name: Check storage base path
88 stat:
89 path: "{{ storage_base_path }}"
90 register: storage_base_stat
91 failed_when: not storage_base_stat.stat.exists
92
93 - name: Check storage base path permissions
94 assert:
95 that:
96 - storage_base_stat.stat.readable
97 - storage_base_stat.stat.writable
98 fail_msg: "Storage base path {{ storage_base_path }} is not readable/writable"
99
100 - name: Check storage docker directory
101 stat:
102 path: "{{ storage_docker_dir }}"
103 register: storage_docker_stat
104 failed_when: not storage_docker_stat.stat.exists
105
106 - name: Check storage docker directory permissions
107 assert:
108 that:
109 - storage_docker_stat.stat.readable
110 - storage_docker_stat.stat.writable
111 fail_msg: "Storage docker directory {{ storage_docker_dir }} is not readable/writable"
112
113 rescue:
114 - name: Display storage path error
115 debug:
116 msg: |
117 ERROR: Storage path validation failed!
118 Path: {{ storage_base_path }}
119 Docker Directory: {{ storage_docker_dir }}
120 Ensure the storage array is mounted and accessible
121 Current mounts: {{ ansible_mounts | map(attribute='mount') | list }}
122 Available space: {{ ansible_mounts | selectattr('mount', 'equalto', storage_base_path) | map(attribute='size_available') | first | default('unknown') | filesizeformat }}
123
124 - name: Display storage deployment information
125 debug:
126 msg: |
127 ð Deploying Storage Services to: {{ inventory_hostname }}
128
129 ð Server Information:
130 - IP Address: {{ ansible_default_ipv4.address }}
131 - Storage Base: {{ storage_base_path }}
132 - Docker Directory: {{ storage_docker_dir }}
133 - User: {{ storage_user }}:{{ storage_group }}
134 - RAID Monitoring: {{ 'Active' if mdmonitor_status is defined and mdmonitor_status.rc == 0 else 'Pending' }}
135 - SMART Monitoring: {{ 'Active' if smartd_status is defined and smartd_status.rc == 0 else 'Pending' }}
136 - GPU Acceleration: Disabled (CPU only)
137 - Available Storage: {{ ansible_mounts | selectattr('mount', 'equalto', storage_base_path) | map(attribute='size_available') | first | default('unknown') | filesizeformat }}
138 - Total Storage: {{ ansible_mounts | selectattr('mount', 'equalto', storage_base_path) | map(attribute='size_total') | first | default('unknown') | filesizeformat }}
139
140 ð¦ Services to Deploy:
141 {% if jellyfin_enabled | default(true) %}
142 - Jellyfin Media Server (Port {{ jellyfin_host_port | default(8096) }}) - CPU Hardware Acceleration
143 {% endif %}
144 {% if arr_stack_enabled | default(true) %}
145 - Arr Stack with Gluetun VPN:
146 * Sonarr TV (Port {{ sonarr_host_port | default(8989) }})
147 * Radarr Movies (Port {{ radarr_host_port | default(7878) }})
148 * Prowlarr Indexers (Port {{ prowlarr_host_port | default(9696) }})
149 * LazyLibrarian Books (Port {{ lazylibrarian_host_port | default(5299) }})
150 * Jellyseer (Port {{ jellyseer_host_port | default(5055) }}) - Media Requests
151 * Flaresolverr (Port {{ flaresolverr_host_port | default(8191) }}) - Cloudflare bypass
152 {% endif %}
153 {% if calibre_enabled | default(true) %}
154 - Calibre Stack:
155 * Calibre Server (Port {{ calibre_server_host_port | default(8083) }})
156 * Calibre-Web (Port {{ calibre_web_host_port | default(8084) }})
157 {% endif %}
158 {% if restic_backup_server_enabled | default(true) %}
159 - Restic Backup Server (Port {{ restic_backup_host_port | default(8000) }})
160 {% endif %}
161
162 ð¡ï¸ Monitoring:
163 - RAID Health Monitoring: Enabled (mdadm)
164 - SMART Monitoring: Enabled
165 - Service Health Checks: Comprehensive
166 - Storage Health: Active monitoring
167
168 ð Directory Structure:
169 - Service configs: {{ storage_docker_dir }}/[service-name]
170 - Environment files: {{ storage_docker_dir }}/[service-name]/.env
171 - Individual service directories for ARR stack
172 - Media Storage: {{ storage_base_path }}/media
173 - Downloads: {{ storage_base_path }}/downloads
174 - Backups: {{ storage_base_path }}/backups
175
176 ð§ Management Tools:
177 - Glances Monitoring: http://{{ ansible_default_ipv4.address }}:61208
178
179 roles:
180 # Core prerequisites
181 - role: user
182 tags: ['core', 'user']
183
184 - role: system
185 tags: ['core', 'system']
186
187 - role: geerlingguy.docker
188 tags: ['core', 'docker']
189
190 - role: geerlingguy.security
191 tags: ['core', 'security']
192
193 - role: docker-framework
194 tags: ['docker-framework', 'core', 'docker']
195
196 # Storage-specific services
197 - role: storage
198 tags: ['storage', 'media', 'backup']
199
200 # RAID monitoring for storage server
201 - role: nas
202 tags: ['storage', 'raid']
203 vars:
204 # Only run monitoring tasks from NAS role
205 nas_nfs_enabled: false
206 nas_network_bonding_enabled: false
207 nas_performance_tuning_enabled: false
208 nas_backup_integration: false
209 nas_enable_runner_exports: false
210 nas_raid_monitoring: true
211 nas_smartmontools_enabled: true
212
213 # Monitoring with RAID support for storage server
214 - role: monitoring
215 tags: ['monitoring', 'glances']
216 vars:
217 monitoring_enabled: true
218 glances_storage_monitoring: true
219 glances_monitored_directories:
220 - path: "/mnt/rstorage"
221 description: "RAID storage array"
222 - path: "/docker"
223 description: "Docker data"
224
225 # Backup role for system-level backups
226 - role: backup
227 tags: ['system-backup']
228 when: backup_enabled | default(true) and ('system-backup' in ansible_run_tags or ansible_run_tags | length == 0)
229
230 post_tasks:
231 - name: Verify core services are running
232 systemd:
233 name: "{{ item }}"
234 state: started
235 enabled: yes
236 loop:
237 - docker
238 - mdmonitor
239 - smartd
240 tags: ['verification']
241
242 - name: Verify RAID monitoring is active
243 command: systemctl is-active mdmonitor
244 register: mdmonitor_status
245 changed_when: false
246 ignore_errors: true
247 tags: ['verification', 'raid']
248
249 - name: Verify SMART monitoring is active
250 command: systemctl is-active smartd
251 register: smartd_status
252 changed_when: false
253 ignore_errors: true
254 tags: ['verification', 'smart']
255
256 - name: Generate storage access summary
257 template:
258 src: "{{ role_path }}/templates/storage-access-summary.txt.j2"
259 dest: "{{ storage_docker_dir }}/storage-access-info.txt"
260 owner: "{{ storage_user | default(ansible_user) }}"
261 group: "{{ storage_group | default(ansible_user) }}"
262 mode: '0644'
263 vars:
264 role_path: "roles/storage"
265 tags: ['summary']
266
267
268 - name: Wait for all storage services to be healthy
269 uri:
270 url: "http://{{ ansible_default_ipv4.address }}:{{ item.port }}{{ item.path | default('') }}"
271 method: GET
272 status_code: [200, 302, 401] # Some services redirect or require auth
273 loop:
274 - { port: "{{ jellyfin_host_port | default(8096) }}", path: "/health", service: "Jellyfin" }
275 - { port: "{{ sonarr_host_port | default(8989) }}", path: "/ping", service: "Sonarr" }
276 - { port: "{{ radarr_host_port | default(7878) }}", path: "/ping", service: "Radarr" }
277 - { port: "{{ prowlarr_host_port | default(9696) }}", path: "/ping", service: "Prowlarr" }
278 - { port: "{{ lazylibrarian_host_port | default(5299) }}", path: "/", service: "LazyLibrarian" }
279 - { port: "{{ jellyseer_host_port | default(5055) }}", path: "/", service: "Jellyseer" }
280 - { port: "{{ flaresolverr_host_port | default(8191) }}", path: "/health", service: "Flaresolverr" }
281 - { port: "{{ calibre_web_host_port | default(8084) }}", path: "/", service: "Calibre-Web" }
282 - { port: "{{ restic_backup_host_port | default(8000) }}", path: "/", service: "Restic Backup" }
283 retries: 15
284 delay: 10
285 ignore_errors: yes
286 tags: ['verification', 'health-check']
287
288 - name: Display deployment completion summary
289 debug:
290 msg: |
291 ð Storage Services Deployment Complete!
292
293 Server: {{ inventory_hostname }} ({{ ansible_default_ipv4.address }})
294
295 ð Monitoring Status:
296 - RAID Monitoring: {{ 'Active' if mdmonitor_status is defined and mdmonitor_status.rc == 0 else 'Pending' }}
297 - SMART Monitoring: {{ 'Active' if smartd_status is defined and smartd_status.rc == 0 else 'Pending' }}
298 - Docker: Active
299
300 ð Access Information:
301 {% if jellyfin_enabled | default(true) %}
302 - Jellyfin: http://{{ ansible_default_ipv4.address }}:{{ jellyfin_host_port | default(8096) }} (CPU Hardware Acceleration)
303 {% endif %}
304 {% if arr_stack_enabled | default(true) %}
305 - Sonarr: http://{{ ansible_default_ipv4.address }}:{{ sonarr_host_port | default(8989) }}
306 - Radarr: http://{{ ansible_default_ipv4.address }}:{{ radarr_host_port | default(7878) }}
307 - Prowlarr: http://{{ ansible_default_ipv4.address }}:{{ prowlarr_host_port | default(9696) }}
308 - LazyLibrarian: http://{{ ansible_default_ipv4.address }}:{{ lazylibrarian_host_port | default(5299) }}
309 - Jellyseer: http://{{ ansible_default_ipv4.address }}:{{ jellyseer_host_port | default(5055) }} (NEW!)
310 - Flaresolverr: http://{{ ansible_default_ipv4.address }}:{{ flaresolverr_host_port | default(8191) }} (Cloudflare bypass)
311 {% endif %}
312 {% if calibre_enabled | default(true) %}
313 - Calibre-Web: http://{{ ansible_default_ipv4.address }}:{{ calibre_web_host_port | default(8084) }}
314 {% endif %}
315 {% if restic_backup_server_enabled | default(true) %}
316 - Restic Backup: http://{{ ansible_default_ipv4.address }}:{{ restic_backup_host_port | default(8000) }}
317 {% endif %}
318
319 ð File Locations:
320 - Docker Configs: {{ storage_docker_dir }}
321 - Media Storage: {{ storage_base_path }}/media
322 - Downloads: {{ storage_base_path }}/downloads
323 - Backups: {{ storage_base_path }}/backups
324 - Access Info: {{ storage_docker_dir }}/storage-access-info.txt
325 - SMART Monitoring: /etc/smartd.conf
326
327 ð¡ï¸ Monitoring Commands:
328 - RAID Status: cat /proc/mdstat
329 - SMART Status: smartctl -a /dev/sdX
330 - Glances: http://{{ ansible_default_ipv4.address }}:61208
331
332 ð§ Next Steps:
333 1. Configure VPN credentials in vault for Gluetun
334 2. Set up indexers in Prowlarr
335 3. Configure quality profiles in Arr applications
336 4. Add media libraries in Jellyfin
337 5. Import books to Calibre library
338 6. Configure Jellyseer with Jellyfin and Arr app integration
339 7. Set up backup clients using: {{ storage_docker_dir }}/restic-server/client-setup-example.sh
340
341 â¡ Management Commands:
342 - Restart All: docker restart $(docker ps -q)
343 - View Logs: docker compose logs -f (in service directories)
344 - Monitor RAID: watch cat /proc/mdstat
345
346 ð¡ Important Notes:
347 - RAID arrays monitored every 5 minutes
348 - SMART monitoring active for drive health
349 - All services configured for CPU-only operation (no GPU)
350 - Individual service directories for ARR stack components
351 tags: ['always']