Compare commits

..

8 commits

Author SHA1 Message Date
a5a6e297ff
feat(nfs): Restrict server to v4 by default
Can be changed with `nfs_v4_only=false` which defaults to true.

Information taken from: https://wiki.debian.org/NFSServerSetup
and applied directly through Ansible.

Currently _irreversible_, meaning once we set the server to v4 only
there is NO ansible-supported playbook to reset it to all NFSv2/3/4
versions.

Has to be done manually, or could be included as manually-run playbook.
2025-11-28 14:19:29 +01:00
ef1823da20
chore(arr): Pin jellyfin to updated version
Moved the jellyfin installation to 10.11.x, so now we should pin it to a
minimum of that. Also, since the 'latest' container for the linuxserver
container images is still the 10.10.7 container, we can't just use that.
So we pin the exact version for now instead.
2025-11-28 14:19:29 +01:00
7f56c80cf4
ref(arr): Pin jellyfin container version
Since jellyfin version 10.11.0 is a _massive_ upgrade, including
non-backwards compatible db migration, we pin the version for now.

See: https://jellyfin.org/posts/jellyfin-release-10.11.0/
2025-11-28 14:19:28 +01:00
9de2aaea48
feat(arr): Move arrstack container versions into vars 2025-11-28 14:19:28 +01:00
e5feb235df
feat(arr): Add fanedits directory to jellyfin media 2025-11-28 14:19:27 +01:00
b855494cf5
feat: Enable ansible pipelining, Disable python warning
Pipelining speeds up the playbook execution. It _can_ have some negative
effects on 'sudo' execution, and specifically requires `requiretty` not
enabled in the sudoers file.

Since this seems (by default) to be the case on debian distributions, I
am trying to switch to pipelining for the time being.
2025-11-28 14:19:26 +01:00
40b687a3f3
feat: Create skeleton for terraform provisioning role
The terraform module does not expect its file contents (project_path) in
the 'files/' folder like the core roles, instead looking for it relative
to the _invocation_ pwd.
So, for now it just resides in the root level of the repository and may
be moved from there to wherever it is more pertinent.

Additionally, we check for the existence of the OpenTofu binary (tofu),
and prefer that if it exists. Otherwise we fall back to the Terraform
binary.
2025-11-28 14:19:26 +01:00
2957b58491
feat: Add Ansible, Terraform gitignore entries 2025-11-28 14:19:25 +01:00
10 changed files with 203 additions and 21 deletions

62
.gitignore vendored
View file

@ -2,3 +2,65 @@ vaultpass
/.ansible
/temp/
# Created by https://www.toptal.com/developers/gitignore/api/-f,linux,markdown,ansible,terraform
# Edit at https://www.toptal.com/developers/gitignore?templates=-f,linux,markdown,ansible,terraform
#!! ERROR: -f is undefined. Use list command to see defined gitignore types !!#
### Ansible ###
*.retry
### Linux ###
*~
# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*
# KDE directory preferences
.directory
# Linux trash folder which might appear on any partition or disk
.Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
#!! ERROR: markdown is undefined. Use list command to see defined gitignore types !!#
### Terraform ###
# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
# Crash log files
crash.log
crash.*.log
# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json
# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Include override files you do wish to add to version control using negated pattern
# !example_override.tf
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*
# Ignore CLI configuration files
.terraformrc
terraform.rc
# End of https://www.toptal.com/developers/gitignore/api/-f,linux,markdown,ansible,terraform

View file

@ -1,4 +1,11 @@
[defaults]
remote_tmp = /tmp
inventory = inventory
roles_path = .ansible/roles:roles
collections_path = .ansible/collections
interpreter_python = auto_silent
[ssh_connection]
pipelining = True

View file

@ -1,7 +1,7 @@
services:
sonarr:
container_name: sonarr
image: lscr.io/linuxserver/sonarr:latest
image: "lscr.io/linuxserver/sonarr:{{ arrstack_sonarr_version }}"
networks:
- caddy
environment:
@ -21,7 +21,7 @@ services:
radarr:
container_name: radarr
image: lscr.io/linuxserver/radarr:latest
image: "lscr.io/linuxserver/radarr:{{ arrstack_radarr_version }}"
networks:
- caddy
environment:
@ -31,7 +31,7 @@ services:
- UMASK_SET={{ arrstack_umask_set }}
volumes:
- "{{ arrstack_env_dir }}/config/radarr:/config"
- "/mnt/ext/data/media/movies:/data/media/movies" # FIXME: Find solution
- "/mnt/ext/data/media/movies:/data/media/movies" # FIXME: Find non-hardcoded solution
- "{{ arrstack_serve_dir }}/files/usenet:/data/usenet"
- "{{ arrstack_serve_dir }}/files/torrent:/data/torrent"
restart: unless-stopped
@ -41,7 +41,7 @@ services:
lidarr:
container_name: lidarr
image: lscr.io/linuxserver/lidarr:latest
image: "lscr.io/linuxserver/lidarr:{{ arrstack_lidarr_version }}"
networks:
- caddy
environment:
@ -66,7 +66,7 @@ services:
readarr:
container_name: readarr
image: lscr.io/linuxserver/readarr:develop
image: "lscr.io/linuxserver/readarr:{{ arrstack_readarr_version }}"
networks:
- caddy
environment:
@ -86,7 +86,7 @@ services:
prowlarr:
container_name: prowlarr
image: lscr.io/linuxserver/prowlarr:develop
image: "lscr.io/linuxserver/prowlarr:{{ arrstack_prowlarr_version }}"
networks:
- caddy
environment:
@ -102,7 +102,7 @@ services:
caddy.reverse_proxy: "{{ '{{' }}upstreams 9696{{ '}}'}}"
beets:
image: lscr.io/linuxserver/beets:latest
image: "lscr.io/linuxserver/beets:{{ arrstack_beets_version }}"
container_name: beets
networks:
- caddy
@ -123,7 +123,7 @@ services:
caddy.reverse_proxy: "{{ '{{' }}upstreams 8337{{ '}}'}}"
tdarr:
image: ghcr.io/haveagitgat/tdarr
image: "ghcr.io/haveagitgat/tdarr:{{ arrstack_tdarr_version }}"
container_name: tdarr
networks:
- caddy
@ -148,6 +148,7 @@ services:
- "{{ arrstack_env_dir }}/data/tdarr:/app/server"
- "{{ arrstack_serve_dir }}/media/tv:/media/tv"
- "/mnt/ext/data/media/movies:/media/movies" # FIXME: To be changed?
- "/mnt/ext/data/media/fanedits:/media/fanedits" # FIXME: Find non-hardcoded solution
- "/transcodes:/transcodes" # TODO: Implement dynamic form with variable?
restart: unless-stopped
devices:
@ -157,7 +158,7 @@ services:
caddy.reverse_proxy: "{{ '{{' }}upstreams 8265{{ '}}'}}"
bazarr:
image: lscr.io/linuxserver/bazarr:latest
image: "lscr.io/linuxserver/bazarr:{{ arrstack_bazarr_version }}"
container_name: bazarr
networks:
- caddy
@ -169,6 +170,7 @@ services:
- "{{ arrstack_env_dir }}/config/sabnzbd:/config"
- "{{ arrstack_serve_dir }}/media/tv:/data/media/tv"
- "/mnt/ext/data/media/movies:/data/media/movies" # FIXME: To be changed?
- "/mnt/ext/data/media/fanedits:/data/media/fanedits" # FIXME: Find non-hardcoded solution
ports:
- 6767:6767
restart: unless-stopped
@ -178,7 +180,7 @@ services:
sabnzbd:
container_name: sabnzbd
image: lscr.io/linuxserver/sabnzbd:latest
image: "lscr.io/linuxserver/sabnzbd:{{ arrstack_sabnzbd_version }}"
networks:
- caddy
environment:
@ -196,7 +198,7 @@ services:
vpn:
container_name: vpn
image: qmcgaw/gluetun:v3
image: "qmcgaw/gluetun:{{ arrstack_gluetun_version }}"
networks:
- caddy
environment:
@ -230,7 +232,7 @@ services:
caddy: "{{ arrstack_qbit_subdomain }}"
caddy.reverse_proxy: "{{ '{{' }}upstreams 8888{{ '}}'}}"
qbittorrent:
image: linuxserver/qbittorrent
image: "linuxserver/qbittorrent:{{ arrstack_qbittorrent_version }}"
container_name: qbittorrent
environment:
- PUID={{ arrstack_puid }}
@ -248,7 +250,7 @@ services:
restart: unless-stopped
homarr:
image: ghcr.io/ajnart/homarr:latest
image: "ghcr.io/ajnart/homarr:{{ arrstack_homarr_version }}"
container_name: homarr
networks:
- caddy
@ -263,7 +265,7 @@ services:
caddy.reverse_proxy: "{{ '{{' }}upstreams 7575{{ '}}'}}"
jellyseerr:
image: fallenbagel/jellyseerr:latest
image: "fallenbagel/jellyseerr:{{ arrstack_jellyseerr_version }}"
container_name: jellyseerr
networks:
- caddy
@ -281,7 +283,7 @@ services:
audiobookshelf:
container_name: audiobookshelf
image: ghcr.io/advplyr/audiobookshelf:latest
image: "ghcr.io/advplyr/audiobookshelf:{{ arrstack_audiobookshelf_version }}"
networks:
- caddy
environment:
@ -300,7 +302,7 @@ services:
caddy.reverse_proxy: "{{ '{{' }}upstreams 80{{ '}}'}}"
jellyfin:
image: lscr.io/linuxserver/jellyfin:latest
image: "lscr.io/linuxserver/jellyfin:{{ arrstack_jellyfin_version }}"
container_name: jellyfin
networks:
- caddy
@ -320,6 +322,7 @@ services:
- "{{ arrstack_env_dir }}/config/jellyfin:/config"
- "{{ arrstack_env_dir }}/data/jellyfin:/config/data"
- "/mnt/ext/data/media/movies:/media/movies" # FIXME: To be changed?
- "/mnt/ext/data/media/fanedits:/media/fanedits" # FIXME: Find non-hardcoded solution
- "{{ arrstack_serve_dir }}/media/tv:/media/tv"
- "{{ arrstack_serve_dir }}/media/music:/media/music"
ports: # FIXME: how to enable discovery behind proxies?
@ -331,7 +334,7 @@ services:
caddy.reverse_proxy: "{{ '{{' }}upstreams 8096{{ '}}'}}"
gonic:
image: sentriz/gonic:latest
image: "sentriz/gonic:{{ arrstack_gonic_version }}"
networks:
- caddy
environment:
@ -351,7 +354,7 @@ services:
{% if restic_enable|d(False) == True and arrstack_restic_enable|d(False) == True %}
backup:
image: mazzolino/restic
image: "mazzolino/restic:{{ arrstack_restic_version }}"
hostname: "{{ ansible_hostname | default() }}"
environment:
TZ: "{{ restic_tz }}"

View file

@ -1,2 +1,20 @@
---
# vars file for arr
arrstack_sonarr_version: latest
arrstack_radarr_version: latest
arrstack_lidarr_version: latest
arrstack_readarr_version: develop
arrstack_prowlarr_version: develop
arrstack_beets_version: latest
arrstack_tdarr_version: latest
arrstack_bazarr_version: latest
arrstack_sabnzbd_version: latest
arrstack_gluetun_version: v3
arrstack_qbittorrent_version: latest
arrstack_homarr_version: latest
arrstack_jellyseerr_version: latest
arrstack_audiobookshelf_version: latest
arrstack_jellyfin_version: "10.11.3"
arrstack_gonic_version: latest
arrstack_restic_version: latest # TODO: Should maybe be set in restic role instead?

View file

@ -0,0 +1,26 @@
---
# role currently only works with opentofu
# Either manually extend to both or just leave out test?
- name: Check if tofu is installed
vars:
terraform_bin: tofu
ansible.builtin.command:
argv:
- which
- "{{ terraform_bin|quote }}"
check_mode: false # run even in check mode
tags: debug
register: tofu_installed
failed_when: false
changed_when: false
- name: Run terraform
community.general.terraform:
binary_path: "{{ (tofu_installed.rc in [ 0 ]) | ternary('tofu', 'terraform') }}"
project_path: "tofu/"
state: present
register: output
- name: Debug output
debug:
var: output

View file

@ -4,3 +4,6 @@ nfs_export_lines:
- "/srv/media 192.168.0.0/24(rw,async,no_subtree_check) 100.112.0.0/16(rw,async,no_subtree_check)"
- "/srv/files 192.168.0.0/24(rw,async,no_subtree_check) 100.112.0.0/16(rw,async,no_subtree_check)"
- "/mnt/ext/data/videos 192.168.0.0/24(rw,async,no_subtree_check) 100.112.0.0/16(rw,async,no_subtree_check)"
nfs_v4_only: true
nfs_v4_disable_rpcbind_fallback: false # needed by Debian 13

View file

@ -10,7 +10,7 @@
ansible.builtin.template:
src: exports.jinja
dest: /etc/exports
mode: '0644'
mode: "0644"
become: true
notify: Reload nfs service
@ -22,6 +22,10 @@
become: true
loop: "{{ nfs_export_lines }}"
- name: Disable NFSv2/NFSv3 to leave NFSv4-only server
ansible.builtin.include_tasks: "nfs-v4-only.yaml"
when: "nfs_v4_only"
- name: Enable nfs server unit
ansible.builtin.systemd:
enabled: true

View file

@ -0,0 +1,48 @@
---
- name: Configure /etc/default/nfs-common for NFSv4-only
ansible.builtin.lineinfile:
path: /etc/default/nfs-common
regexp: '^(# *)?{{ item.key }}=.*'
line: '{{ item.key }}={{ item.val }}'
loop:
- { key: NEED_STATD, val: '"no"' }
- { key: NEED_IDMAPD, val: '"yes"' }
become: true
notify: Reload nfs service
- name: Configure /etc/default/nfs-kernel-server for NFSv4-only
ansible.builtin.lineinfile:
path: /etc/default/nfs-kernel-server
regexp: '^(# *)?{{ item.key }}=.*'
line: '{{ item.key }}={{ item.val }}'
create: true # in case the file or the var is missing
loop:
- { key: RPCNFSDOPTS, val: '"--no-nfs-version 2 --no-nfs-version 3"' }
- { key: RPCMOUNTDOPTS, val: '"--manage-gids --no-nfs-version 2 --no-nfs-version 3"' }
become: true
notify: Reload nfs service
# This _can_ be used on very modern kernels, but disables
# the rpcbind fallback if nfsdctl lockd configuration fails.
# Debian 13 still requires this so it is disabled by default
- name: Mask rpcbind units (not needed for NFSv4)
ansible.builtin.systemd:
name: "{{ item }}"
masked: true
state: stopped
loop:
- rpcbind.service
- rpcbind.socket
become: true
when: "nfs_v4_disable_rpcbind_fallback"
- name: Unmask rpcbind units to keep as fallback
ansible.builtin.systemd:
name: "{{ item }}"
masked: false
state: started
loop:
- rpcbind.socket
- rpcbind.service
become: true
when: "not nfs_v4_disable_rpcbind_fallback"

View file

@ -5,7 +5,6 @@
gather_facts: False
become: true
tags:
- system
- bootstrap
tasks:
- name: check for python
@ -50,6 +49,13 @@
# name: incus-install
# tags: incus
- name: Raise infrastructure
hosts: localhost
tags: infrastructure
tasks:
- ansible.builtin.import_role:
name: infrastructure
# ansible-galaxy install geerlingguy.docker
- name: Install docker
hosts: instance_system

5
tofu/main.tf Normal file
View file

@ -0,0 +1,5 @@
output "my_debug_output" {
description = "just debuggin"
value = 42
}