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>
This commit is contained in:
298
ansible-deployment.yml
Normal file
298
ansible-deployment.yml
Normal file
@@ -0,0 +1,298 @@
|
||||
---
|
||||
# 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
|
||||
Reference in New Issue
Block a user