Files
arr-suite-template-bootstrap/ansible-deployment.yml
openhands 24f2cd64e9 Initial template repository
🎬 ARR Suite Template Bootstrap - Complete Media Automation Stack

Features:
- 16 production services (Prowlarr, Sonarr, Radarr, Plex, etc.)
- One-command Ansible deployment
- VPN-protected downloads via Gluetun
- Tailscale secure access
- Production-ready security (UFW, Fail2Ban)
- Automated backups and monitoring
- Comprehensive documentation

Ready for customization and deployment to any VPS.

Co-authored-by: openhands <openhands@all-hands.dev>
2025-11-28 04:26:12 +00:00

298 lines
9.7 KiB
YAML

---
# Ansible Playbook for *arr Stack Deployment
# Based on successful VPS deployment at YOUR_VPS_IP_ADDRESS
#
# Usage: ansible-playbook -i inventory/production.yml ansible-deployment.yml
#
# This playbook deploys a complete media automation stack including:
# - Prowlarr (indexer management)
# - Sonarr (TV shows)
# - Radarr (movies)
# - Lidarr (music)
# - Whisparr (adult content)
# - Bazarr (subtitles)
# - Jellyseerr (request management)
# - SABnzbd (usenet downloader)
# - Deluge (torrent downloader)
# - Plex (media server)
# - Tautulli (Plex analytics)
# - Gluetun (VPN container)
- name: Deploy Complete *arr Media Stack
hosts: all
become: yes
vars:
# Stack configuration
stack_name: "arr-stack"
base_path: "/home/docker"
compose_file: "{{ base_path }}/compose/docker-compose.yml"
# Network configuration
tailscale_ip: "YOUR_TAILSCALE_IP" # Your current Tailscale IP
# Service ports (current working configuration)
services:
prowlarr: 9696
sonarr: 8989
radarr: 7878
lidarr: 8686
whisparr: 6969
bazarr: 6767
jellyseerr: 5055
sabnzbd: 8080
deluge: 8081
plex: 32400
tautulli: 8181
# VPN configuration (parameterized)
vpn_provider: "{{ vault_vpn_provider | default('nordvpn') }}"
vpn_username: "{{ vault_vpn_username }}"
vpn_password: "{{ vault_vpn_password }}"
# API Keys (to be generated or provided)
api_keys:
prowlarr: "{{ vault_prowlarr_api_key | default('') }}"
sonarr: "{{ vault_sonarr_api_key | default('') }}"
radarr: "{{ vault_radarr_api_key | default('') }}"
lidarr: "{{ vault_lidarr_api_key | default('') }}"
whisparr: "{{ vault_whisparr_api_key | default('') }}"
bazarr: "{{ vault_bazarr_api_key | default('') }}"
jellyseerr: "{{ vault_jellyseerr_api_key | default('') }}"
sabnzbd: "{{ vault_sabnzbd_api_key | default('') }}"
tasks:
- name: System Requirements Check
block:
- name: Check OS version
ansible.builtin.setup:
filter: ansible_distribution*
- name: Verify minimum requirements
ansible.builtin.assert:
that:
- ansible_memtotal_mb >= 4096
- ansible_architecture == "x86_64"
fail_msg: "System does not meet minimum requirements (4GB RAM, x86_64)"
success_msg: "System requirements verified"
- name: Display system info
ansible.builtin.debug:
msg: |
System: {{ ansible_distribution }} {{ ansible_distribution_version }}
Memory: {{ ansible_memtotal_mb }}MB
Architecture: {{ ansible_architecture }}
Disk Space: {{ ansible_mounts[0].size_total // 1024 // 1024 // 1024 }}GB
- name: Docker Environment Setup
block:
- name: Install Docker dependencies
ansible.builtin.apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg
- lsb-release
state: present
update_cache: yes
- name: Add Docker GPG key
ansible.builtin.apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
- name: Add Docker repository
ansible.builtin.apt_repository:
repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
state: present
- name: Install Docker
ansible.builtin.apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
state: present
update_cache: yes
- name: Start and enable Docker
ansible.builtin.systemd:
name: docker
state: started
enabled: yes
- name: Add user to docker group
ansible.builtin.user:
name: "{{ ansible_user }}"
groups: docker
append: yes
- name: Create Directory Structure
block:
- name: Create base directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0755'
loop:
- "{{ base_path }}"
- "{{ base_path }}/compose"
- "{{ base_path }}/prowlarr"
- "{{ base_path }}/sonarr"
- "{{ base_path }}/radarr"
- "{{ base_path }}/lidarr"
- "{{ base_path }}/whisparr"
- "{{ base_path }}/bazarr"
- "{{ base_path }}/jellyseerr"
- "{{ base_path }}/sabnzbd"
- "{{ base_path }}/deluge"
- "{{ base_path }}/plex"
- "{{ base_path }}/tautulli"
- "{{ base_path }}/gluetun"
- "{{ base_path }}/media"
- "{{ base_path }}/downloads"
- name: Deploy Docker Compose Configuration
block:
- name: Generate docker-compose.yml
ansible.builtin.template:
src: docker-compose.yml.j2
dest: "{{ compose_file }}"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0644'
notify: restart stack
- name: Create environment file
ansible.builtin.template:
src: .env.j2
dest: "{{ base_path }}/compose/.env"
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0600'
notify: restart stack
- name: Deploy Stack
block:
- name: Pull latest images
ansible.builtin.command:
cmd: docker compose pull
chdir: "{{ base_path }}/compose"
become_user: "{{ ansible_user }}"
- name: Start the stack
ansible.builtin.command:
cmd: docker compose up -d
chdir: "{{ base_path }}/compose"
become_user: "{{ ansible_user }}"
- name: Wait for services to be ready
ansible.builtin.wait_for:
host: "{{ tailscale_ip }}"
port: "{{ item.value }}"
timeout: 300
loop: "{{ services | dict2items }}"
when: item.key != 'plex' # Plex binds to 0.0.0.0
- name: Wait for Plex specifically
ansible.builtin.wait_for:
host: "0.0.0.0"
port: "{{ services.plex }}"
timeout: 300
- name: Verify Deployment
block:
- name: Check container health
ansible.builtin.command:
cmd: docker ps --filter "status=running" --format "table {{.Names}}\t{{.Status}}"
register: container_status
become_user: "{{ ansible_user }}"
- name: Display container status
ansible.builtin.debug:
var: container_status.stdout_lines
- name: Test service endpoints
ansible.builtin.uri:
url: "http://{{ tailscale_ip }}:{{ item.value }}"
method: GET
status_code: [200, 302, 307, 403] # Accept various redirect/auth responses
loop: "{{ services | dict2items }}"
when: item.key != 'plex'
register: service_tests
- name: Test Plex endpoint
ansible.builtin.uri:
url: "http://{{ ansible_default_ipv4.address }}:{{ services.plex }}/web"
method: GET
status_code: [200, 302, 307]
register: plex_test
- name: Display test results
ansible.builtin.debug:
msg: "All services are responding correctly!"
when: service_tests is succeeded and plex_test is succeeded
handlers:
- name: restart stack
ansible.builtin.command:
cmd: docker compose down && docker compose up -d
chdir: "{{ base_path }}/compose"
become_user: "{{ ansible_user }}"
# Post-deployment configuration tasks
- name: Configure Service Integrations
hosts: all
become: no
vars:
prowlarr_url: "http://{{ tailscale_ip }}:9696"
sonarr_url: "http://{{ tailscale_ip }}:8989"
radarr_url: "http://{{ tailscale_ip }}:7878"
tasks:
- name: Wait for API endpoints to be ready
ansible.builtin.uri:
url: "{{ item }}/api/v1/system/status"
method: GET
headers:
X-Api-Key: "{{ api_keys[item.split(':')[2].split('/')[0]] }}"
status_code: 200
loop:
- "{{ prowlarr_url }}"
- "{{ sonarr_url }}"
- "{{ radarr_url }}"
retries: 10
delay: 30
when: api_keys[item.split(':')[2].split('/')[0]] != ""
- name: Display post-deployment information
ansible.builtin.debug:
msg: |
🎉 *arr Stack Deployment Complete!
📊 Service URLs:
- Prowlarr: {{ prowlarr_url }}
- Sonarr: {{ sonarr_url }}
- Radarr: {{ radarr_url }}
- Lidarr: http://{{ tailscale_ip }}:8686
- Whisparr: http://{{ tailscale_ip }}:6969
- Bazarr: http://{{ tailscale_ip }}:6767
- Jellyseerr: http://{{ tailscale_ip }}:5055
- SABnzbd: http://{{ tailscale_ip }}:8080
- Deluge: http://{{ tailscale_ip }}:8081
- Plex: http://{{ ansible_default_ipv4.address }}:32400
- Tautulli: http://{{ tailscale_ip }}:8181
🔧 Next Steps:
1. Configure indexers in Prowlarr
2. Set up download clients in *arr apps
3. Configure media libraries in Plex
4. Set up request workflows in Jellyseerr
📁 Data Locations:
- Config: {{ base_path }}/[service-name]
- Media: {{ base_path }}/media
- Downloads: {{ base_path }}/downloads