/
/
/
Ansible role that provisions my storage server.
1---
2# Storage Services Role Defaults
3# Comprehensive media automation and backup configuration
4
5# ==============================================================================
6# GENERAL SETTINGS
7# ==============================================================================
8storage_enabled: true
9storage_docker_dir: "/docker"
10storage_base_path: "/mnt/rstorage"
11storage_user: "{{ ansible_user }}"
12storage_group: "users"
13storage_uid: 1000
14storage_gid: 100
15
16# Timezone configuration
17storage_timezone: "{{ system_timezone }}"
18
19# Docker network for storage services
20storage_docker_network: "storage-network"
21storage_network_subnet: "172.21.0.0/16"
22
23# Arr Stack Docker Network (isolated from storage)
24arr_docker_network: "arr-network"
25arr_network_subnet: "172.22.0.0/16"
26
27# Configuration directories (used by prerequisites for directory creation)
28storage_config_directories:
29 - "{{ storage_docker_dir }}"
30 - "{{ jellyfin_data_dir }}"
31 - "{{ jellyfin_config_dir }}"
32 - "{{ jellyfin_cache_dir }}"
33 - "{{ arr_config_dir }}"
34 - "{{ sonarr_config_dir }}"
35 - "{{ radarr_config_dir }}"
36 - "{{ prowlarr_config_dir }}"
37 - "{{ lazylibrarian_config_dir }}"
38 - "{{ bazarr_config_dir }}"
39 - "{{ jellyseer_config_dir }}"
40 - "{{ flaresolverr_config_dir }}"
41 - "{{ qbittorrent_config_dir }}"
42 - "{{ gluetun_config_dir }}"
43 - "{{ lidarr_config_dir }}"
44 - "{{ cleanuparr_config_dir }}"
45 - "{{ navidrome_config_dir }}"
46 - "{{ navidrome_data_dir }}"
47 - "{{ gdu_config_dir }}"
48 - "{{ calibre_config_dir }}"
49 - "{{ calibre_server_config_dir }}"
50 - "{{ calibre_web_config_dir }}"
51 - "{{ restic_backup_config_dir }}"
52 - "{{ nextcloud_config_dir }}"
53 - "{{ nextcloud_mariadb_config_dir }}"
54 - "{{ nextcloud_redis_config_dir }}"
55 - "{{ nextcloud_elasticsearch_config_dir }}"
56 - "{{ nextcloud_collabora_config_dir }}"
57
58# ==============================================================================
59# SERVICE CONFIGURATIONS
60# ==============================================================================
61
62# Jellyfin Media Server
63jellyfin_enabled: true
64jellyfin_port: 8096
65jellyfin_host_port: 8096
66jellyfin_data_dir: "{{ storage_docker_dir }}/jellyfin"
67jellyfin_config_dir: "{{ jellyfin_data_dir }}/config"
68jellyfin_cache_dir: "{{ jellyfin_data_dir }}/cache"
69jellyfin_media_dir: "{{ storage_base_path }}/media"
70
71# --- Hardware acceleration (Intel iGPU / VAAPI)
72jellyfin_hwaccel_enabled: true
73jellyfin_dri_device: "/dev/dri"
74jellyfin_render_group_gid: "109" # render group on host
75jellyfin_video_group_gid: "44" # video group on host
76
77# Arr Stack Configuration
78arr_stack_enabled: true
79arr_vpn_enabled: true
80arr_downloads_dir: "{{ storage_base_path }}/downloads"
81arr_config_dir: "{{ storage_docker_dir }}/media-automation"
82
83# Sonarr (TV Shows)
84sonarr_enabled: true
85sonarr_port: 8989
86sonarr_host_port: 8989
87sonarr_config_dir: "{{ arr_config_dir }}/sonarr"
88sonarr_tv_dir: "{{ storage_base_path }}/media/shows"
89
90# Radarr (Movies)
91radarr_enabled: true
92radarr_port: 7878
93radarr_host_port: 7878
94radarr_config_dir: "{{ arr_config_dir }}/radarr"
95radarr_movies_dir: "{{ storage_base_path }}/media/movies"
96
97# Prowlarr (Indexers)
98prowlarr_enabled: true
99prowlarr_port: 9696
100prowlarr_host_port: 9696
101prowlarr_config_dir: "{{ arr_config_dir }}/prowlarr"
102
103# LazyLibrarian (Books) - Replaced Readarr
104# Uses a custom Dockerfile that adds Calibre for ebook conversion
105lazylibrarian_enabled: true
106lazylibrarian_port: 5299
107lazylibrarian_host_port: 5299
108lazylibrarian_config_dir: "{{ arr_config_dir }}/lazy-librarian"
109lazylibrarian_books_dir: "{{ storage_base_path }}/media/books"
110lazylibrarian_custom_build: true
111lazylibrarian_custom_image: "my/lazylibrarian-with-calibre:latest"
112
113# Jellyseer (Media Requests)
114jellyseer_enabled: true
115jellyseer_port: 5055
116jellyseer_host_port: 5055
117jellyseer_config_dir: "{{ arr_config_dir }}/jellyseer"
118
119# Bazarr (Subtitles)
120bazarr_enabled: true
121bazarr_port: 6767
122bazarr_host_port: 6767
123bazarr_config_dir: "{{ arr_config_dir }}/bazarr"
124bazarr_movies_dir: "{{ storage_base_path }}/media/movies"
125bazarr_shows_dir: "{{ storage_base_path }}/media/shows"
126
127# Flaresolverr (Cloudflare bypass)
128flaresolverr_enabled: true
129flaresolverr_port: 8191
130flaresolverr_host_port: 8191
131flaresolverr_config_dir: "{{ arr_config_dir }}/flaresolverr"
132
133# qBittorrent (Download Client)
134qbittorrent_enabled: true
135qbittorrent_port: 8080
136qbittorrent_host_port: 8085
137qbittorrent_config_dir: "{{ arr_config_dir }}/qbittorrent"
138qbittorrent_downloads_dir: "{{ arr_downloads_dir }}"
139
140# Lidarr (Music)
141lidarr_enabled: true
142lidarr_port: 8686
143lidarr_host_port: 8686
144lidarr_config_dir: "{{ arr_config_dir }}/lidarr"
145lidarr_music_dir: "{{ storage_base_path }}/media/music"
146
147# Cleanuparr (Automated download cleanup for Sonarr/Radarr)
148cleanuparr_enabled: true
149cleanuparr_port: 11011
150cleanuparr_host_port: 11011
151cleanuparr_config_dir: "{{ arr_config_dir }}/cleanuparr"
152
153# Gluetun VPN for Arr Stack
154gluetun_enabled: "{{ arr_vpn_enabled }}"
155gluetun_config_dir: "{{ arr_config_dir }}/gluetun"
156
157# VPN Provider Settings (from vault)
158vpn_service_provider: "{{ vault_storage.vpn_service_provider | default('') }}"
159vpn_server_countries: "Netherlands,Switzerland"
160vpn_server_regions: "Europe"
161
162# Firewall - allow arr services to reach LAN (Jellyfin, Calibre, etc.)
163firewall_outbound_subnets: "192.168.34.0/24"
164
165# Calibre Stack Configuration
166calibre_enabled: true
167calibre_config_dir: "{{ storage_docker_dir }}/calibre"
168calibre_library_dir: "{{ storage_base_path }}/media/books"
169
170# Calibre Server
171calibre_server_enabled: true
172calibre_desktop_gui_port: 8080
173calibre_desktop_gui_host_port: 6080
174calibre_https_desktop_gui_port: 8181
175calibre_https_desktop_gui_host_port: 6181
176calibre_server_port: 8081
177calibre_server_host_port: 6081
178calibre_server_config_dir: "{{ calibre_config_dir }}/server"
179
180# Calibre-Web
181calibre_web_enabled: true
182calibre_web_port: 8083
183calibre_web_host_port: 6083
184calibre_web_config_dir: "{{ calibre_config_dir }}/web"
185
186# ==============================================================================
187# NEXTCLOUD CONFIGURATION
188# ==============================================================================
189nextcloud_enabled: false
190nextcloud_port: 80
191nextcloud_host_port: 8443
192nextcloud_config_dir: "{{ storage_docker_dir }}/nextcloud"
193nextcloud_data_dir: "{{ storage_base_path }}/nextcloud"
194nextcloud_mariadb_config_dir: "{{ nextcloud_config_dir }}/db"
195nextcloud_redis_config_dir: "{{ nextcloud_config_dir }}/redis"
196nextcloud_elasticsearch_config_dir: "{{ nextcloud_config_dir }}/elasticsearch"
197nextcloud_collabora_config_dir: "{{ nextcloud_config_dir }}/collabora"
198
199# Nextcloud database (from vault)
200nextcloud_db_name: "nextcloud"
201nextcloud_db_user: "{{ vault_storage.nextcloud_db_user | default('') }}"
202nextcloud_db_password: "{{ vault_storage.nextcloud_db_password | default('') }}"
203nextcloud_db_root_password: "{{ vault_storage.nextcloud_db_root_password | default('') }}"
204
205# Nextcloud admin (from vault)
206nextcloud_admin_user: "{{ vault_storage.nextcloud_admin_user | default('') }}"
207nextcloud_admin_password: "{{ vault_storage.nextcloud_admin_password | default('') }}"
208
209# Nextcloud trusted domains (space-separated in the container env)
210nextcloud_trusted_domains:
211 - "localhost"
212 - "{{ ansible_default_ipv4.address | default('127.0.0.1') }}"
213 - "storage.home"
214 - "nextcloud.home"
215 - "nextcloud"
216
217# Collabora Online (document editing)
218nextcloud_collabora_domain: "nextcloud\\.home"
219nextcloud_collabora_extra_params: "--o:ssl.enable=false --o:ssl.termination=false"
220nextcloud_collabora_port: 9980
221nextcloud_collabora_host_port: 9980
222
223# Public URL for Collabora (browser must reach this to load the editor)
224nextcloud_collabora_public_url: "http://nextcloud.home:{{ nextcloud_collabora_host_port }}"
225
226# Elasticsearch (full-text search via fulltextsearch app)
227nextcloud_elasticsearch_password: "{{ vault_storage.nextcloud_elasticsearch_password | default('changeme') }}"
228
229# Nextcloud PHP tuning
230nextcloud_php_memory_limit: "512M"
231nextcloud_php_upload_limit: "16G"
232
233# Nextcloud resource limits
234nextcloud_memory_limit: "2g"
235nextcloud_cpu_limit: "2"
236nextcloud_db_memory_limit: "1g"
237nextcloud_db_cpu_limit: "1"
238nextcloud_redis_memory_limit: "256m"
239nextcloud_redis_cpu_limit: "0.5"
240nextcloud_collabora_memory_limit: "2g"
241nextcloud_collabora_cpu_limit: "2"
242nextcloud_elasticsearch_memory_limit: "2g"
243nextcloud_elasticsearch_cpu_limit: "1"
244nextcloud_imaginary_memory_limit: "1g"
245nextcloud_imaginary_cpu_limit: "1"
246
247# ==============================================================================
248# NAVIDROME CONFIGURATION
249# ==============================================================================
250navidrome_enabled: true
251navidrome_port: 4533
252navidrome_host_port: 4533
253navidrome_config_dir: "{{ storage_docker_dir }}/navidrome"
254navidrome_data_dir: "{{ navidrome_config_dir }}/data"
255navidrome_music_dir: "{{ storage_base_path }}/media/music"
256
257# ==============================================================================
258# GDU DISK USAGE ANALYZER CONFIGURATION
259# ==============================================================================
260# Browser UI (gdu --web) over the RAID array. The array is mounted read-only
261# and deletion is disabled, so the UI can only ever inspect.
262# The scan runs once at container start and there is no rescan in the UI:
263# restarting the container is the rescan (~830k files at time of writing).
264gdu_enabled: true
265gdu_port: 8080
266gdu_host_port: 8098
267gdu_config_dir: "{{ storage_docker_dir }}/gdu"
268gdu_scan_dir: "{{ storage_base_path }}"
269# gdu answers only localhost, literal IPs, or the ONE hostname named in
270# --web-listen (DNS-rebinding guard). Naming storage.home there - mapped to
271# 0.0.0.0 inside the container - keeps binding on all interfaces while making
272# http://storage.home:<port> work. Every other name still gets a 403.
273gdu_web_hostname: "{{ ansible_host | default(inventory_hostname) }}"
274gdu_max_cores: 4 # leave headroom for Jellyfin/arr during a scan
275gdu_memory_limit: "2g" # whole tree is held in memory
276
277# ==============================================================================
278# RESTIC BACKUP SERVER CONFIGURATION
279# ==============================================================================
280restic_backup_server_enabled: true
281restic_backup_port: 8000
282restic_backup_host_port: 8000
283restic_backup_config_dir: "{{ storage_docker_dir }}/restic-server"
284restic_backup_data_dir: "{{ storage_base_path }}/backups"
285
286# Restic server authentication (from vault)
287restic_backup_username: "{{ vault_storage.restic_backup_username | default('') }}"
288restic_backup_password: "{{ vault_storage.restic_backup_password | default('') }}"
289
290# ==============================================================================
291# VOLUME MOUNTS AND PATHS
292# ==============================================================================
293
294# Common media directories (only what services actually use)
295media_directories:
296 - "{{ storage_base_path }}/media"
297 - "{{ storage_base_path }}/media/movies"
298 - "{{ storage_base_path }}/media/shows"
299 - "{{ storage_base_path }}/media/books"
300 - "{{ storage_base_path }}/media/music"
301 - "{{ storage_base_path }}/downloads"
302 - "{{ storage_base_path }}/backups"
303 - "{{ nextcloud_data_dir }}"
304
305# Docker configuration directories
306docker_config_directories:
307 - "{{ storage_docker_dir }}"
308 - "{{ jellyfin_data_dir }}"
309 - "{{ jellyfin_config_dir }}"
310 - "{{ jellyfin_cache_dir }}"
311 - "{{ arr_config_dir }}"
312 - "{{ sonarr_config_dir }}"
313 - "{{ radarr_config_dir }}"
314 - "{{ prowlarr_config_dir }}"
315 - "{{ lazylibrarian_config_dir }}"
316 - "{{ bazarr_config_dir }}"
317 - "{{ jellyseer_config_dir }}"
318 - "{{ flaresolverr_config_dir }}"
319 - "{{ qbittorrent_config_dir }}"
320 - "{{ gluetun_config_dir }}"
321 - "{{ lidarr_config_dir }}"
322 - "{{ cleanuparr_config_dir }}"
323 - "{{ navidrome_config_dir }}"
324 - "{{ navidrome_data_dir }}"
325 - "{{ gdu_config_dir }}"
326 - "{{ calibre_config_dir }}"
327 - "{{ calibre_server_config_dir }}"
328 - "{{ calibre_web_config_dir }}"
329 - "{{ restic_backup_config_dir }}"
330 - "{{ nextcloud_config_dir }}"
331 - "{{ nextcloud_mariadb_config_dir }}"
332 - "{{ nextcloud_redis_config_dir }}"
333 - "{{ nextcloud_elasticsearch_config_dir }}"
334 - "{{ nextcloud_collabora_config_dir }}"
335
336# ==============================================================================
337# SECURITY AND ACCESS SETTINGS
338# ==============================================================================
339
340# User credentials (from vault)
341jellyfin_admin_username: "{{ vault_storage.jellyfin_admin_username | default('') }}"
342jellyfin_admin_password: "{{ vault_storage.jellyfin_admin_password | default('') }}"
343calibre_web_admin_username: "{{ vault_storage.calibre_web_admin_username | default('') }}"
344calibre_web_admin_password: "{{ vault_storage.calibre_web_admin_password | default('') }}"
345
346# ==============================================================================
347# PERFORMANCE TUNING
348# ==============================================================================
349
350# Docker resource limits (can be overridden per service)
351default_memory_limit: "2g"
352default_cpu_limit: "2"
353
354# Service-specific resource limits (following runner pattern)
355jellyfin_memory_limit: "4g"
356jellyfin_cpu_limit: "4"
357arr_stack_memory_limit: "1g"
358arr_stack_cpu_limit: "1"
359calibre_stack_memory_limit: "1g"
360calibre_stack_cpu_limit: "1"
361restic_server_memory_limit: "512m"
362restic_server_cpu_limit: "0.5"
363
364# ==============================================================================
365# MONITORING AND HEALTH CHECKS
366# ==============================================================================
367health_check_enabled: true
368health_check_interval: "30s"
369health_check_timeout: "10s"
370health_check_retries: 3
371health_check_start_period: "60s"
372
373# ==============================================================================
374# BACKUP CONFIGURATION
375# ==============================================================================
376# Backups are handled by roles/backup (backup.yml), scheduled in Semaphore.
377# What to back up / exclude is set in host_vars (backup_service_excludes,
378# backup_data_paths); schedule and retention are not configured here.
379
380# ==============================================================================
381# SERVICE HEALTH ENDPOINTS
382# ==============================================================================
383service_endpoints:
384 jellyfin: "http://localhost:{{ jellyfin_port }}/health"
385 sonarr: "http://localhost:{{ sonarr_port }}"
386 radarr: "http://localhost:{{ radarr_port }}"
387 prowlarr: "http://localhost:{{ prowlarr_port }}"
388 lazylibrarian: "http://localhost:{{ lazylibrarian_port }}"
389 lidarr: "http://localhost:{{ lidarr_port }}"
390 jellyseer: "http://localhost:{{ jellyseer_port }}"
391 bazarr: "http://localhost:{{ bazarr_port }}"
392 flaresolverr: "http://localhost:{{ flaresolverr_port }}"
393 qbittorrent: "http://localhost:{{ qbittorrent_port }}"
394 calibre_server: "http://localhost:{{ calibre_server_port }}"
395 calibre_web: "http://localhost:{{ calibre_web_port }}"
396 cleanuparr: "http://localhost:{{ cleanuparr_port }}/health"
397 navidrome: "http://localhost:{{ navidrome_port }}"
398 gdu: "http://localhost:{{ gdu_host_port }}"
399 restic_server: "http://localhost:{{ restic_backup_port }}"
400 nextcloud: "http://localhost:{{ nextcloud_port }}/status.php"
401
402# ==============================================================================
403# CONTAINER IMAGES (fallback pins)
404# ==============================================================================
405# The authoritative pins live in server_automation/group_vars/all/images.yml,
406# which overrides these. They are kept here, identical, so the role stays
407# deterministic on its own. Never define images in vars/main.yml: role vars
408# beat group_vars and would silently ignore the central pins.
409jellyfin_image: "docker.io/jellyfin/jellyfin"
410jellyfin_version: "10.11.6"
411sonarr_image: "lscr.io/linuxserver/sonarr"
412sonarr_version: "4.0.15.2941-ls291"
413radarr_image: "lscr.io/linuxserver/radarr"
414radarr_version: "5.27.5.10198-ls283"
415prowlarr_image: "lscr.io/linuxserver/prowlarr"
416prowlarr_version: "2.0.5.5160-ls125"
417lazylibrarian_image: "lscr.io/linuxserver/lazylibrarian"
418lazylibrarian_version: "e7c7ce2d-ls262"
419lidarr_image: "lscr.io/linuxserver/lidarr"
420lidarr_version: "3.1.0.4875-ls20"
421gluetun_image: "docker.io/qmcgaw/gluetun"
422gluetun_version: "latest@sha256:156ed7abe05c2e8cbe505e699909e2fe1779c71d977df5936484b692c61d3717"
423calibre_image: "lscr.io/linuxserver/calibre"
424calibre_version: "v7.19.0-ls304"
425calibre_web_image: "lscr.io/linuxserver/calibre-web"
426calibre_web_version: "0.6.25-ls352"
427restic_image: "docker.io/restic/rest-server"
428restic_version: "0.14.0"
429jellyseer_image: "docker.io/fallenbagel/jellyseerr"
430jellyseer_version: "2.7.3"
431bazarr_image: "lscr.io/linuxserver/bazarr"
432bazarr_version: "v1.5.3-ls322"
433qbittorrent_image: "lscr.io/linuxserver/qbittorrent"
434qbittorrent_version: "5.1.2-r1-ls413"
435cleanuparr_image: "ghcr.io/cleanuparr/cleanuparr"
436cleanuparr_version: "2.8.1"
437navidrome_image: "docker.io/deluan/navidrome"
438navidrome_version: "0.60.3"
439flaresolverr_image: "ghcr.io/flaresolverr/flaresolverr"
440flaresolverr_version: "v3.4.1"
441gdu_image: "ghcr.io/dundee/gdu"
442gdu_version: "v5.38.0"
443nextcloud_image: "docker.io/library/nextcloud"
444nextcloud_version: "30.0.17-apache"
445nextcloud_mariadb_image: "docker.io/library/mariadb"
446nextcloud_mariadb_version: "11.8.6"
447nextcloud_redis_image: "docker.io/library/redis"
448nextcloud_redis_version: "7.4.8-alpine"
449nextcloud_collabora_image: "docker.io/collabora/code"
450nextcloud_collabora_version: "25.04.9.4.1"
451nextcloud_elasticsearch_image: "docker.io/library/elasticsearch"
452nextcloud_elasticsearch_version: "7.17.27"
453nextcloud_imaginary_image: "docker.io/nextcloud/aio-imaginary"
454nextcloud_imaginary_version: "20250325_084656"
455