/
/
/
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 - Health monitoring will be provided by netdata (to be deployed separately)
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 # Backup role for system-level backups
214 - role: backup
215 tags: ['system-backup']
216 when: backup_enabled | default(true)
217
218 post_tasks:
219 - name: Verify core services are running
220 systemd:
221 name: "{{ item }}"
222 state: started
223 enabled: yes
224 loop:
225 - docker
226 - mdmonitor
227 - smartd
228 tags: ['verification']
229
230 - name: Verify RAID monitoring is active
231 command: systemctl is-active mdmonitor
232 register: mdmonitor_status
233 changed_when: false
234 ignore_errors: true
235 tags: ['verification', 'raid']
236
237 - name: Verify SMART monitoring is active
238 command: systemctl is-active smartd
239 register: smartd_status
240 changed_when: false
241 ignore_errors: true
242 tags: ['verification', 'smart']
243
244 - name: Generate storage access summary
245 template:
246 src: "{{ role_path }}/templates/storage-access-summary.txt.j2"
247 dest: "{{ storage_docker_dir }}/storage-access-info.txt"
248 owner: "{{ storage_user | default(ansible_user) }}"
249 group: "{{ storage_group | default(ansible_user) }}"
250 mode: '0644'
251 vars:
252 role_path: "roles/storage"
253 tags: ['summary']
254
255
256 - name: Wait for all storage services to be healthy
257 uri:
258 url: "http://{{ ansible_default_ipv4.address }}:{{ item.port }}{{ item.path | default('') }}"
259 method: GET
260 status_code: [200, 302, 401] # Some services redirect or require auth
261 loop:
262 - { port: "{{ jellyfin_host_port | default(8096) }}", path: "/health", service: "Jellyfin" }
263 - { port: "{{ sonarr_host_port | default(8989) }}", path: "/ping", service: "Sonarr" }
264 - { port: "{{ radarr_host_port | default(7878) }}", path: "/ping", service: "Radarr" }
265 - { port: "{{ prowlarr_host_port | default(9696) }}", path: "/ping", service: "Prowlarr" }
266 - { port: "{{ lazylibrarian_host_port | default(5299) }}", path: "/", service: "LazyLibrarian" }
267 - { port: "{{ jellyseer_host_port | default(5055) }}", path: "/", service: "Jellyseer" }
268 - { port: "{{ flaresolverr_host_port | default(8191) }}", path: "/health", service: "Flaresolverr" }
269 - { port: "{{ calibre_web_host_port | default(8084) }}", path: "/", service: "Calibre-Web" }
270 - { port: "{{ restic_backup_host_port | default(8000) }}", path: "/", service: "Restic Backup" }
271 retries: 15
272 delay: 10
273 ignore_errors: yes
274 tags: ['verification', 'health-check']
275
276 - name: Display deployment completion summary
277 debug:
278 msg: |
279 ð Storage Services Deployment Complete!
280
281 Server: {{ inventory_hostname }} ({{ ansible_default_ipv4.address }})
282
283 ð Monitoring Status:
284 - RAID Monitoring: {{ 'Active' if mdmonitor_status is defined and mdmonitor_status.rc == 0 else 'Pending' }}
285 - SMART Monitoring: {{ 'Active' if smartd_status is defined and smartd_status.rc == 0 else 'Pending' }}
286 - Docker: Active
287
288 ð Access Information:
289 {% if jellyfin_enabled | default(true) %}
290 - Jellyfin: http://{{ ansible_default_ipv4.address }}:{{ jellyfin_host_port | default(8096) }} (CPU Hardware Acceleration)
291 {% endif %}
292 {% if arr_stack_enabled | default(true) %}
293 - Sonarr: http://{{ ansible_default_ipv4.address }}:{{ sonarr_host_port | default(8989) }}
294 - Radarr: http://{{ ansible_default_ipv4.address }}:{{ radarr_host_port | default(7878) }}
295 - Prowlarr: http://{{ ansible_default_ipv4.address }}:{{ prowlarr_host_port | default(9696) }}
296 - LazyLibrarian: http://{{ ansible_default_ipv4.address }}:{{ lazylibrarian_host_port | default(5299) }}
297 - Jellyseer: http://{{ ansible_default_ipv4.address }}:{{ jellyseer_host_port | default(5055) }} (NEW!)
298 - Flaresolverr: http://{{ ansible_default_ipv4.address }}:{{ flaresolverr_host_port | default(8191) }} (Cloudflare bypass)
299 {% endif %}
300 {% if calibre_enabled | default(true) %}
301 - Calibre-Web: http://{{ ansible_default_ipv4.address }}:{{ calibre_web_host_port | default(8084) }}
302 {% endif %}
303 {% if restic_backup_server_enabled | default(true) %}
304 - Restic Backup: http://{{ ansible_default_ipv4.address }}:{{ restic_backup_host_port | default(8000) }}
305 {% endif %}
306
307 ð File Locations:
308 - Docker Configs: {{ storage_docker_dir }}
309 - Media Storage: {{ storage_base_path }}/media
310 - Downloads: {{ storage_base_path }}/downloads
311 - Backups: {{ storage_base_path }}/backups
312 - Access Info: {{ storage_docker_dir }}/storage-access-info.txt
313 - SMART Monitoring: /etc/smartd.conf
314
315 ð¡ï¸ Monitoring Commands:
316 - RAID Status: cat /proc/mdstat
317 - SMART Status: smartctl -a /dev/sdX
318 - Service Health: Will be monitored by netdata (to be deployed separately)
319
320 ð§ Next Steps:
321 1. Configure VPN credentials in vault for Gluetun
322 2. Set up indexers in Prowlarr
323 3. Configure quality profiles in Arr applications
324 4. Add media libraries in Jellyfin
325 5. Import books to Calibre library
326 6. Configure Jellyseer with Jellyfin and Arr app integration
327 7. Set up backup clients using: {{ storage_docker_dir }}/restic-server/client-setup-example.sh
328
329 â¡ Management Commands:
330 - Restart All: docker restart $(docker ps -q)
331 - View Logs: docker compose logs -f (in service directories)
332 - Monitor RAID: watch cat /proc/mdstat
333
334 ð¡ Important Notes:
335 - RAID arrays monitored every 5 minutes
336 - SMART monitoring active for drive health
337 - All services configured for CPU-only operation (no GPU)
338 - Individual service directories for ARR stack components
339 tags: ['always']