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