diff options
39 files changed, 494 insertions, 112 deletions
@@ -1,6 +1,6 @@ # My Homelab Setup -**This is still very much un-stable, and very much a work-in-progress.** +**This is project is now somewhat stable but still a work-in-progress.** So I decided to migrate *most* of my remote VPS to a self-hosted private server. There are 3 reasons for this: 1. Cost savings. VPS SSD storage can get quite [expensive](https://racknerd.com/kvm-vps) as your storage demands increase. Meanwhile I have a few SSDs lying my house around not being utilized. @@ -14,13 +14,14 @@ My requirements are as follows: | | Email | n/a | | :heavy_check_mark: | Syncing/Sharing files, calender, contacts, tasks etc. | [Nextcloud](https://nextcloud.com/) | | :heavy_check_mark: | Hosting misc. static websites | [Nginx](https://www.nginx.com/) | -| :heavy_check_mark: | Media (pictures, tv, movies) Server | [Jellyfin](https://jellyfin.org/) | +| :heavy_check_mark: | Media (pictures, tv, movies) Server | [Plex](https://plex.tv) or [Jellyfin](https://jellyfin.org/) | | :heavy_check_mark: | Meta Search Engine | [Searxng](https://docs.searxng.org/) | | :heavy_check_mark: | Music Server | [Navidrome](https://www.navidrome.org/) | | :heavy_check_mark: | Network accessible storage for media | [Samba](https://www.samba.org/) | | :heavy_check_mark: | Password Hosting/Share | [Bitwarden](https://bitwarden.com/) | | :heavy_check_mark: | VPN | [Wireguard](https://www.wireguard.com/) | | :heavy_check_mark: | Torrents | [Transmission](https://transmissionbt.com/) | +| :heavy_check_mark: | Photo sharing | [Photoprism](https://photoprism.app) | | :heavy_check_mark: | Media downloader | [Sonarr](https://github.com/Sonarr/Sonarr), [Radarr](https://github.com/Radarr/Radarr), [Prowlarr](https://github.com/Prowlarr/Prowlarr) | > Consider items marked as :heavy_check_mark: above to be implemented in this repo. @@ -32,7 +33,7 @@ I don't want it to have much of an impact on my utility (power) bill, which of c so much heat that it requires advanced cooling. ## Architecture - + Notice that this setup requires a very small VPS since we are just running a wireguard client on it. All storage, and compute is being done by the Home Server. This means that my cost went from ~ $13/mo to ~ $1/mo for the VPS service, *while* increasing my SSD storage capacity from 50GB to 2TB (or whatever you have lying around)! And this does not incude the cost savings from self-hosting many of these services in the first place. @@ -46,71 +47,99 @@ There are many cheap VPS providers out there. I use Racknerd now. I have used Vu ## Pre-requisites ### Equipment -1. Raspberry Pi 4B (4GB). -2. USB Hub with **with its own power source**. -3. 2 X TiB external usb SSDs or HDDs for our network-attached-storage (NAS). -4. (optional, but recommended) 1 external usb SSD or HDD as a backup drive. -5. A very cheap VPS - I use Racknerd. +> The equipment I use reflect my current priorities and have changed since this project started and will +continue to change in the future. + +#### NAS Equipment +A [ROCKPro64](https://wiki.pine64.org/wiki/ROCKPro64) 4G model. +Alternatively, an RPi4 or similar would also work if you can get your hands on it. + + +There are many reasons why this board is awesome: +1. In Stock. +2. Similar price/performance/energy usage as the RPi4. +3. PCIe slot!! + +My main reason for switching to this board is the final point above :) +Speaking of the PCIe slot, we can get a PCIe SATA adapter to connect to two SATA drives. + + + +To power the drives, Pine64 all sells power cables: + + + +The drives themselves are 4 TiB 3.5in 5400 RPM CMR such as: + + + +> 7200 RPM drives are more expensive and I won't be pushing this hard enough to notice the benefits. + +> Note there will be cheaper drives using SMR, but I'd recommend CMR is worth the extra cost. + +Remember to buy drives that are not made as part of the same batch, so that the probabity of them +dying together is the lowest. You can also do this by buying different brands. + +Also remember to buy an extra set so that you save yourself some panic during recovery. + +When using the dual SATA adapter, Pine64 recommends a beefier 12V 5A power-supply: + + + +Since this is basically a NAS build, I opted to house all of this in: + + + + +Which comes included with a fan, and the SATA power-cables. + +#### Backup Equipment +An external USB SSD or HDD as a backup drive. + +#### VPS +A VPS (Virtual Private Server) - I use Racknerd. This will give you access to a reliable static IP. ### Base Image setup -For numerous reasons, I have switched to using Alpine Linux `aarch64` as the base OS image. -Going forward this repo will primarily be geared towards it. -That being said, all existing roles will continue to work on Raspi OS (debian) based images as well. - -1. Follow instructions from [Alpine wiki](https://wiki.alpinelinux.org/wiki/Raspberry_Pi) to create a bootable SD card. -2. During the `setup-alpine` process, select the **sys** mode for a persistent install. -3. Reboot -4. -```bash -# TODO: Make these tasks part of an Ansible role. -echo "dtoverlay=gpio-fan,gpiopin=14,temp=70000" > /boot/usercfg.txt - -# enable community packages in /etc/apk/repositories -# enable http://dl-cdn.alpinelinux.org/alpine/edge/testing -apk update -apk upgrade -apk add vim nnn htop bmon rsync py3-pip -apk add wireguard-tools docker docker-compose certbot -modprobe wireguard -# add wireguard to /etc/modules # - -# docker stuff -addgroup username docker -rc-update add docker boot -service docker start -``` -5. Continue to the NAS Setup section. +Once you have a server+storage, and a VPS, you are ready to start. +I have switched to using DietPi images (from Alpine) as they support my hardware, as well as RPis. + +1. Follow instructions from [DietPi wiki](https://dietpi.com/docs/install/) to create a bootable SD card and boot up. +2. On first boot, I recommend you change your `root` password, and change the SSH server to use OpenSSH (from Dropbear). +This makes running automation against it easier. +3. Then you can run our playbook with the `base` tag to install all pre-requisite software. +See the Install section below for getting started with running playbooks from this repo. +4. You should then setup your drives according to your needs. If you are doing ZFS, the section below might be useful. +4. Then see the Deploy section below to start deploying your apps! ### NAS Setup -As a minimum, I recommend setting up two of the external usb drives as a ZFS mirrored pool. +I recommend setting up the two drives as a ZFS mirrored pool. This will allow you to lose up to one drive at a time, and still operate in a degraded state while you replace it. ```bash -# install zfs -apk add udev zfs -modprobe zfs -# add zfs to /etc/modules # +# Install zfs (the base role will do this for you if using a rockpro64) +# Otherwise there are usually two steps: +# 1. Install the kernel headers for your kernel. +# 2. Install the zfs-dkms, and zfsutils-linux packages. + # verify install zfs --version -rc-update add udev sysinit -rc-update add udev-trigger sysinit # assuming your first drive is /dev/sda -zpool create yourpool /dev/sda -zpool export yourpool -zpool import -d /dev/disk/by-id/usb-Samsung_PSSD_T7_Shield_S6SMNS0T701007V-0:0-part1 yourpool -N +# get the ids -> /dev/sdX mapping of your disks by +ls -al /dev/disk/by-id/ +# using ids is recommened because then zpool status will instantly tell you +# which disk in reality is problematic, rather than sda,sdb etc. +zpool create yourpool mirror /dev/disk/by-id/yourdisk1 /dev/disk/by-id/yourdisk2 zfs set mountpoint=/mnt/yourpool yourpool # create datasets zfs create yourpool/media zfs create yourpool/data # for encrypted datasets zfs create -o encryption=on -o keylocation=prompt -o keyformat=passphrase yourpool/priv -# add the second drive to the pool -zpool attach yourpool usb-Samsung_PSSD_T7_Shield_S6SMNS0T701007V-0:0 usb-Samsung_PSSD_T7_S5T3NJ0N907662T-0:0 # zfs useful cmds -zpool scrub yourpool zpool status -v yourpool +zpool scrub yourpool zfs get mountpoint zfs mount -l yourpool/data zfs mount -l yourpool/media @@ -218,3 +247,4 @@ echo "-------Backup ended-------" ## Why self-host? You can read more about my thoughts on why we should self-host on my [website](https://kumardamani.net). +If this repo has helped you, consider [sending](https://kumardamani.net/#donate) a coffee my way! diff --git a/group_vars/all/vars.yaml b/group_vars/all/vars.yaml index 12fd063..7b27440 100644 --- a/group_vars/all/vars.yaml +++ b/group_vars/all/vars.yaml @@ -1,9 +1,21 @@ +# The value in this file correspond to MY SETUP. +# You will need to change these to reflect yours. --- +# This is the secondary user you want to setup. +# Passwords go in the Vault. +username: 'kdam0' + +# This is pretty much only used for the ZFS install step. +# sbc: Currently supported ones are: + # - Rockchip: rockchip64 +# If you don't care about installing ZFS from here, leave this blank. +hardware: 'rockchip64' + # the wildcard domain under which all apps will be hosted. apps_domain: 'kumardamani.net' -# list all the app configs you want to host here. +# list of all the apps you want to run behind nginx. # port: the host port that this service will be available on. # access_domain: external (nginx) domain. # Only include when applicable. @@ -52,6 +64,16 @@ apps_conf_map: port: 31907 status_endpoint: '/system/status' curl_requires_auth: false + photoprism: + port: 31908 + access_domain: 'photos.{{ apps_domain }}' + status_endpoint: '' + curl_requires_auth: false + # adguard: + # port: 31909 + # access_domain: 'adguard.{{ apps_domain }}' + # status_endpoint: '' + # curl_requires_auth: false # list all static websites you plan to host here. # name: the root domain name of the static website. diff --git a/group_vars/all/vault b/group_vars/all/vault index b7497af..328c155 100644 --- a/group_vars/all/vault +++ b/group_vars/all/vault @@ -1,28 +1,34 @@ $ANSIBLE_VAULT;1.1;AES256 -63336336613233616539616164616433666436353761376139336663613032323161616536336332 -3963653737353730383561343463623437353230376561350a633133373035393636363762643066 -30613830346231326161666661643065316439323632633434633836326136303339386236386561 -6462346562313464300a633266363066383138396333656338656432633465353365316635613337 -66336532643561363230346164343636663331333436383536653339306332343731386430636262 -33333638333565306331363866343763353434613230646631386234393762366236343633653431 -33613133623664646135616439616165366531303565616439326330646335326536396634316165 -63656431613766643434383565666262356261373937313961376538353037306236653730643564 -35613564393762366335303532366338393930613936383137363435633331323635383837663864 -32663866366131303263636162303837643161306433346433353361616263376265613934653934 -33336161626137343432633561313631396262626665356633343561636133386630323466383636 -34636434396464386265326638303730656166353663343561313737353130333635356337623639 -35373562613765346339303066323136386139326230633732653437623239386132376165386634 -32613162323931346238303334616137356264326663613362366664396162393033343664306663 -34656435316430373534646636653662303962396130323365643966666461613861343131656633 -32666432616238326338613862623638386338303030323365666237616162306133656136623633 -36343964656333663665373062386664626438303866643533303961346431376331373436376339 -31333131363762316563626164356236343862373533633839666133343337653237343730313965 -64333633336630653935636436323132643861666338613631323433353764306266326333653436 -30333661333834323262366464656166373066343035343335636332346561656463616266333866 -39383464343837656562666565393434663363316639323032333330383334616638393463653863 -37643162626335376666666336633339386434633532343765653334333033393830666630656633 -64383730666662363331663965656139336638343532663030343362623134376661313539313132 -30646437616134616538396464636361646435633130333137386332643931303234653738343531 -63363063333233376334326538393233366436353230613430326562643632313738356333383430 -61343262346531656537313965626532656664396139386331623362333538353561303465303466 -616362393632643064653731333335383438 +61373432623236353766373265633331353163393733366637363333383935386137336464313864 +6165363331303839363837396331353866373264376464660a363364666661613738336536333138 +37383833663765366135366139323531353966663333313938636163643636666638383863353265 +6638363065323261350a343063343932633066313962323763623366333232316262383662393562 +63656138336339373431333033643264393937306137356466343161396362353537323363643834 +64326264326665636633636361386336316235386433326536613263666234363932323766316538 +64306362353236316334653839303636373963343434303435343132326638613162323334663232 +37663932646132613935333030386633343833323532343864616136393737303864343139643366 +61316666346439613762376534663162643030356630646661623037643063306632343861636336 +31663435323362663163616335383264656534363739366461373239396337623234616164366239 +63306530616163633735633062666134393430633733333235373134663763666637323930346130 +66373663653734356665313464633966313835303838323266613331313535353064663431396133 +65616135376163653532356438313331396138653164356362313633633935623435383430663538 +32316162643436333933343537373233343731383164363634373964653537613265633934643235 +65656636643133333735346164373862383161363061306638363763363333653562653334323536 +62323066653462346362323737626436303966393932633132636264623032633530653763636434 +33393966306436396564356563666635666630626661313762336134643232646631353234393864 +63613165396362643963303632636531653939333134363032313533663735613337386236313034 +62653139633861383662356530396638333837613338666563343835346236653036313964643330 +30353063356636643331396635633438313335396133356431613232326630363864393939663962 +38313861356163646665346163613932626662623636333165386535613863363264613666636432 +39326665323563613033663930636631616264626236656265616337383737616466643530653463 +63346538316231313365373866373430623131366561326137653535663437623935626636636338 +38303463666639646166323834353334383831376465363532616330313737393262656666393566 +65336239653861313433386661636339356532623862306238653366373531333833323237623731 +63356466323637343830356164393833323633343234376262313564613861303535613864336531 +37643536333339633366363264303638316266316163343039376537373634626361393163326565 +65373539643461383066306363313165616435633836363336313336396666336232626136636331 +34623434343431646134353734373733343338346432343333316132366566633861373934326434 +65333130633463626364386561636232383138313632626639653063303163646137323336396635 +61363066663465613731373936663337666563616662626234643239396335376363393431306638 +36323665633236376465663662643230313564386561386462326337323533613762663435363763 +33383135636437353363313030663561393235363366323030383166393939333332 diff --git a/group_vars/all/vault.example b/group_vars/all/vault.example index 8ca22bf..e053dda 100644 --- a/group_vars/all/vault.example +++ b/group_vars/all/vault.example @@ -1,5 +1,7 @@ --- +vault_user_password: '' + vault_wireguard_server_priv_key: '' vault_ddclient_user: '' @@ -18,3 +20,6 @@ vault_searxng_secret_key: '' vault_transmission_user: '' vault_transmission_password: '' +vault_photoprism_admin_password: '' + +vault_plex_claim: '' diff --git a/architecture.png b/pix/architecture.png Binary files differindex e7a092d..e7a092d 100644 --- a/architecture.png +++ b/pix/architecture.png diff --git a/architecture.xml b/pix/architecture.xml index 6a254c4..6a254c4 100644 --- a/architecture.xml +++ b/pix/architecture.xml diff --git a/pix/board.png b/pix/board.png Binary files differnew file mode 100644 index 0000000..b02b9f8 --- /dev/null +++ b/pix/board.png diff --git a/pix/case.jpg b/pix/case.jpg Binary files differnew file mode 100644 index 0000000..1d264a8 --- /dev/null +++ b/pix/case.jpg diff --git a/pix/case.png b/pix/case.png Binary files differnew file mode 100644 index 0000000..122172e --- /dev/null +++ b/pix/case.png diff --git a/pix/pcie_sata.png b/pix/pcie_sata.png Binary files differnew file mode 100644 index 0000000..a7cfba9 --- /dev/null +++ b/pix/pcie_sata.png diff --git a/pix/psu.png b/pix/psu.png Binary files differnew file mode 100644 index 0000000..7143f87 --- /dev/null +++ b/pix/psu.png diff --git a/pix/sata_power.png b/pix/sata_power.png Binary files differnew file mode 100644 index 0000000..cc5450f --- /dev/null +++ b/pix/sata_power.png diff --git a/pix/wd.png b/pix/wd.png Binary files differnew file mode 100644 index 0000000..1583750 --- /dev/null +++ b/pix/wd.png diff --git a/playbooks/deploy.yaml b/playbooks/deploy.yaml index 08af211..e1e0970 100644 --- a/playbooks/deploy.yaml +++ b/playbooks/deploy.yaml @@ -5,26 +5,33 @@ # make dry-run file=playbooks/deploy.yaml tags="" # # Available tags: -# ddclient, wireguard, nginx, websites, -# samba, nextcloud, bitwarden, searxng, +# base, ddclient, wireguard, websites samba, +# nginx, nextcloud, bitwarden, searxng, # monitoring, transmission, media_downloader +# photoprism, plex # # Install: # make install # -# ASSUMPTIONS: -# You should already have docker installed. -# See the README.md for base image setup section. -# --- ## Non Docker Services ## - hosts: compute gather_facts: true + tags: 'base' + become: true + roles: + - role: base + vars: + base_user: '{{ username }}' + base_user_password: '{{ vault_user_password }}' + base_hardware: '{{ hardware }}' + +- hosts: compute + gather_facts: true tags: 'ddclient' become: true - become_method: 'doas' roles: - role: ddclient vars: @@ -37,7 +44,6 @@ gather_facts: true tags: 'wireguard' become: true - become_method: 'doas' roles: - role: wireguard vars: @@ -47,9 +53,9 @@ wireguard_server_ip: '192.168.10.1/32' wireguard_peers: - peer_ip: '192.168.10.2/32' # my phone - peer_key: '/mFv6y9QA8dhX/A9fFn+mzg/SZq4BZPeNofm1w754y8=' + peer_key: 'QUirl0QsXC3P6D5t5VPlvDgfji1ZI5e0XhppBFLW9TY=' - peer_ip: '192.168.10.3/32' # personal laptop - peer_key: '6+t6FbEHSAOuzBtQwb0bDqfFa5Kvfkb2QIUGpRKA5Eg=' + peer_key: 'pzqY/UxffV8O01K982jlgP1xJQphnglOdqVhOM2IUSc=' - peer_ip: '192.168.10.4/32' # vps peer_key: 'oz2jxeSUWD4r5g3y7XuSItqSiqOOJz8vulYYVXAsKS8=' @@ -70,7 +76,6 @@ gather_facts: true tags: 'samba' become: true - become_method: 'doas' roles: - role: samba vars: @@ -94,6 +99,17 @@ nginx_static_html_root: '{{ vol_data }}/sites' nginx_certs_root: '{{ vol_data }}/letsencrypt' +# - hosts: compute +# gather_facts: false +# tags: 'adguard' +# become: false +# roles: +# - role: adguard +# vars: +# adguard_domain: '{{ apps_conf_map["adguard"]["access_domain"] }}' +# adguard_host_port: '{{ apps_conf_map["adguard"]["port"] }}' +# adguard_data_root: '{{ vol_data }}/adguard' + - hosts: compute gather_facts: false tags: 'nextcloud' @@ -163,3 +179,30 @@ monitoring_root_dir: '{{ vol_data }}/monitoring' monitoring_apps_confs: '{{ apps_conf_map | dict2items(key_name="app", value_name="conf") }}' monitoring_static_sites: '{{ nginx_websites }}' + +- hosts: compute + gather_facts: false + tags: 'photoprism' + become: false + roles: + - role: photoprism + vars: + photoprism_host_port: '{{ apps_conf_map["photoprism"]["port"] }}' + photoprism_admin_password: '{{ vault_photoprism_admin_password }}' + photoprism_data_dir: '{{ vol_data }}/photoprism' + photoprism_pics_root_dir: '{{ vol_media }}/pics' + +- hosts: compute + gather_facts: false + tags: 'plex' + become: false + roles: + - role: plex + vars: + plex_host_port: '{{ apps_conf_map["plex"]["port"] }}' + plex_claim: '{{ vault_plex_claim }}' + plex_library_dir: '{{ vol_media }}/plex' + plex_tv_dir: '{{ vol_media }}/media/tv' + plex_mov_dir: '{{ vol_media }}/media/mov' + plex_pic_dir: '{{ vol_media }}/pics' + plex_music_dir: '{{ vol_media }}/music' diff --git a/roles/adguard/defaults/main.yaml b/roles/adguard/defaults/main.yaml new file mode 100644 index 0000000..91abefa --- /dev/null +++ b/roles/adguard/defaults/main.yaml @@ -0,0 +1,4 @@ +# Default Values +--- + +adguard_docker_network_name: 'adguard_network' diff --git a/roles/adguard/handlers/main.yaml b/roles/adguard/handlers/main.yaml new file mode 100644 index 0000000..cd21505 --- /dev/null +++ b/roles/adguard/handlers/main.yaml @@ -0,0 +1,2 @@ +--- + diff --git a/roles/adguard/tasks/main.yaml b/roles/adguard/tasks/main.yaml new file mode 100644 index 0000000..018b067 --- /dev/null +++ b/roles/adguard/tasks/main.yaml @@ -0,0 +1,38 @@ +# https://docs.photoprism.app/getting-started/docker-compose/ +--- + +- name: 'Create the AdGuard network' + community.docker.docker_network: + name: '{{ adguard_docker_network_name }}' + state: 'present' + +- name: 'Create the required dirs' + file: + state: 'directory' + path: '{{ item }}' + mode: '755' + loop: + - '{{ adguard_data_root }}' + - '{{ adguard_data_root }}/data' + - '{{ adguard_data_root }}/conf' + +- name: 'Start the AdGuard container' + community.docker.docker_container: + name: 'adguard' + image: 'adguard/adguardhome' + pull: true + state: 'started' + recreate: false + networks: + - name: '{{ adguard_docker_network_name }}' + ports: + # DNS + - '53:53/tcp' + - '53:53/udp' + # Web + - '{{ adguard_host_port }}:3000' + volumes: + - '{{ adguard_data_root }}/data:/opt/adguardhome/work' + - '{{ adguard_data_root }}/conf:/opt/adguardhome/conf' + # restart_policy: 'unless-stopped' + restart_policy: 'no' diff --git a/roles/base/defaults/main.yaml b/roles/base/defaults/main.yaml new file mode 100644 index 0000000..9a163d4 --- /dev/null +++ b/roles/base/defaults/main.yaml @@ -0,0 +1,2 @@ +# Default Values +--- diff --git a/roles/base/handlers/main.yaml b/roles/base/handlers/main.yaml new file mode 100644 index 0000000..cd21505 --- /dev/null +++ b/roles/base/handlers/main.yaml @@ -0,0 +1,2 @@ +--- + diff --git a/roles/base/tasks/base_alpine.yaml b/roles/base/tasks/base_alpine.yaml new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/roles/base/tasks/base_alpine.yaml @@ -0,0 +1 @@ +--- diff --git a/roles/base/tasks/base_debian.yaml b/roles/base/tasks/base_debian.yaml new file mode 100644 index 0000000..9d25ea6 --- /dev/null +++ b/roles/base/tasks/base_debian.yaml @@ -0,0 +1,62 @@ +--- + +- name: 'Install base packages for Debian' + apt: + name: + - '{{ item }}' + state: 'present' + update_cache: true + loop: + - 'less' + - 'vim' + - 'tmux' + - 'neofetch' + - 'iotop-c' + - 'tcpdump' + - 'rsync' + - 'python3-pip' + - 'docker.io' + - 'docker-compose' + +- name: 'Install base packages for Python' + pip: + name: '{{ item }}' + loop: + - 'docker' + - 'pexpect' + +- name: "Create the user '{{ base_user }}'" + user: + name: '{{ base_user }}' + state: 'present' + shell: '/bin/bash' + uid: 1001 + password: '{{ base_user_password | password_hash("sha512") }}' + create_home: true + generate_ssh_key: true + groups: 'sudo, docker' + append: true + +- name: 'Allow passwordless sudo for this {{ base_user }}' + lineinfile: + path: '/etc/sudoers' + state: 'present' + regexp: '^%{{ base_user }}\s+ALL=' + line: '%{{ base_user }} ALL=(ALL) NOPASSWD: ALL' + validate: '/usr/sbin/visudo -cf %s' + +- block: + - name: 'Install kernel headers for currently installed kernel' + apt: + name: 'linux-headers-current-rockchip64' + state: 'present' + + - name: 'Install zfs modules and package' + apt: + name: '{{ item }}' + state: 'present' + default_release: 'bullseye-backports' + loop: + - 'zfs-dkms' + - 'zfsutils-linux' + when: 'base_hardware == "rockchip64"' diff --git a/roles/base/tasks/main.yaml b/roles/base/tasks/main.yaml new file mode 100644 index 0000000..4930d10 --- /dev/null +++ b/roles/base/tasks/main.yaml @@ -0,0 +1,9 @@ +--- + +- name: 'Run base install for Alpine' + import_tasks: 'base_alpine.yaml' + when: 'ansible_os_family == "Alpine"' + +- name: 'Run base install for Debian' + import_tasks: 'base_debian.yaml' + when: 'ansible_os_family == "Debian"' diff --git a/roles/ddclient/tasks/main.yaml b/roles/ddclient/tasks/main.yaml index d780a3f..defcef8 100644 --- a/roles/ddclient/tasks/main.yaml +++ b/roles/ddclient/tasks/main.yaml @@ -15,9 +15,16 @@ update_cache: true when: 'ansible_os_family == "Debian"' +# - name: 'Update ddclient conf' +# template: +# dest: '/etc/ddclient/ddclient.conf' +# src: 'ddclient.conf.j2' +# mode: '644' +# notify: 'restart ddclient' + - name: 'Update ddclient conf' template: - dest: '/etc/ddclient/ddclient.conf' + dest: '/etc/ddclient.conf' src: 'ddclient.conf.j2' mode: '644' notify: 'restart ddclient' diff --git a/roles/media_downloader/tasks/main.yaml b/roles/media_downloader/tasks/main.yaml index d3840a5..5f6c8c8 100644 --- a/roles/media_downloader/tasks/main.yaml +++ b/roles/media_downloader/tasks/main.yaml @@ -15,8 +15,8 @@ state: 'started' recreate: false env: - "PUID": '1000' - "PGID": '1000' + "PUID": '1001' + "PGID": '1001' "TZ": 'America/Toronto' networks: - name: '{{ media_downloader_docker_network_name }}' @@ -36,8 +36,8 @@ state: 'started' recreate: false env: - "PUID": '1000' - "PGID": '1000' + "PUID": '1001' + "PGID": '1001' "TZ": 'America/Toronto' networks: - name: '{{ media_downloader_docker_network_name }}' @@ -57,8 +57,8 @@ state: 'started' recreate: false env: - "PUID": '1000' - "PGID": '1000' + "PUID": '1001' + "PGID": '1001' "TZ": 'America/Toronto' networks: - name: '{{ media_downloader_docker_network_name }}' @@ -76,8 +76,8 @@ state: 'started' recreate: false env: - "PUID": '1000' - "PGID": '1000' + "PUID": '1001' + "PGID": '1001' "TZ": 'America/Toronto' "UMASK": '002' networks: diff --git a/roles/monitoring/files/zfs-exporter.service b/roles/monitoring/files/zfs-exporter.service new file mode 100644 index 0000000..8982723 --- /dev/null +++ b/roles/monitoring/files/zfs-exporter.service @@ -0,0 +1,13 @@ +[Unit] +Description=ZFS Exporter +After=network.target + +[Service] +Type=simple +ExecStart=/usr/bin/zfs_exporter +LimitNOFILE=infinity +User=root +Group=root + +[Install] +WantedBy=multi-user.target diff --git a/roles/monitoring/tasks/node.yaml b/roles/monitoring/tasks/node.yaml index 5d38f1f..632ceb8 100644 --- a/roles/monitoring/tasks/node.yaml +++ b/roles/monitoring/tasks/node.yaml @@ -21,3 +21,18 @@ state: 'started' enabled: true when: 'ansible_os_family == "Alpine"' + +- block: + - name: 'Install node-exporter' + become: true + package: + name: 'prometheus-node-exporter' + state: 'present' + + - name: 'Start and enable node-exporter service' + become: true + service: + name: 'prometheus-node-exporter' + state: 'started' + enabled: true + when: 'ansible_os_family == "Debian"' diff --git a/roles/monitoring/tasks/zfs.yaml b/roles/monitoring/tasks/zfs.yaml index 2f18e32..a30a7ac 100644 --- a/roles/monitoring/tasks/zfs.yaml +++ b/roles/monitoring/tasks/zfs.yaml @@ -17,13 +17,13 @@ mode: '755' - name: 'Extract ZFS exporter to dir' - command: 'tar -xf zfs_exporter-2.2.5.linux-arm64.tar.gz' + command: 'tar -xf zfs_exporter.tar.gz' args: chdir: '{{ monitoring_root_dir }}' - name: 'Copy the executable to /usr/bin' become: true - become_method: 'doas' + # become_method: 'doas' copy: src: '{{ monitoring_root_dir }}/zfs_exporter-2.2.5.linux-arm64/zfs_exporter' dest: '/usr/bin/zfs_exporter' @@ -31,18 +31,31 @@ owner: 'root' remote_src: true -- name: 'Create service file to start zfs_exporter on boot' +# - name: 'Create service file to start zfs_exporter on boot' +# become: true +# # become_method: 'doas' +# copy: +# src: 'zfs-exporter' +# dest: '/etc/init.d/zfs-exporter' +# mode: '0755' +# owner: 'root' +# +- name: 'Create systemd services' become: true - become_method: 'doas' copy: - src: 'zfs-exporter' - dest: '/etc/init.d/zfs-exporter' - mode: '0755' - owner: 'root' + src: 'zfs-exporter.service' + dest: '/etc/systemd/system/zfs-exporter.service' + mode: '644' + # notify: + # - 'zfs-exporter restart' + +- name: 'Reload systemd units' + become: true + systemd: + daemon_reload: true - name: 'Start and enable the zfs-exporter service' become: true - become_method: 'doas' service: name: 'zfs-exporter' state: 'started' diff --git a/roles/nextcloud/defaults/main.yaml b/roles/nextcloud/defaults/main.yaml index cd11c66..b0c20f3 100644 --- a/roles/nextcloud/defaults/main.yaml +++ b/roles/nextcloud/defaults/main.yaml @@ -1,8 +1,8 @@ # Default Values --- -nextcloud_uid: "1000" -nextcloud_gid: "1000" +nextcloud_uid: "1001" +nextcloud_gid: "1001" nextcloud_tz: 'America/Toronto' nextcloud_data_root: '' nextcloud_host_port: '' diff --git a/roles/nextcloud/tasks/main.yaml b/roles/nextcloud/tasks/main.yaml index c1d91aa..2354d4b 100644 --- a/roles/nextcloud/tasks/main.yaml +++ b/roles/nextcloud/tasks/main.yaml @@ -35,7 +35,7 @@ - name: 'Make sure the Nextcloud container is created and running' community.docker.docker_container: name: '{{ nextcloud_container_name }}' - image: 'ghcr.io/linuxserver/nextcloud:php8-24.0.4-ls81' + image: 'lscr.io/linuxserver/nextcloud:25.0.2' pull: true state: 'started' env: diff --git a/roles/nginx_docker/templates/app.nginx.conf.j2 b/roles/nginx_docker/templates/app.nginx.conf.j2 index 82c3610..f6f92de 100644 --- a/roles/nginx_docker/templates/app.nginx.conf.j2 +++ b/roles/nginx_docker/templates/app.nginx.conf.j2 @@ -1,5 +1,6 @@ server { listen 443 ssl; + client_max_body_size 500M; root _; server_name {{ item.conf.access_domain }}; ssl_certificate /etc/letsencrypt/live/{{ nginx_global_apps_domain }}/fullchain.pem; @@ -18,8 +19,13 @@ server { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Host $server_name; proxy_set_header X-Forwarded-Proto https; +{% if item.app == "photoprism" %} {# Photoprism requires websockets #} + proxy_buffering off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; +{% endif %} proxy_read_timeout 1200s; - client_max_body_size 0; error_log /var/log/nginx/{{ item.app }}.error.log; } } diff --git a/roles/photoprism/defaults/main.yaml b/roles/photoprism/defaults/main.yaml new file mode 100644 index 0000000..29e3605 --- /dev/null +++ b/roles/photoprism/defaults/main.yaml @@ -0,0 +1,4 @@ +# Default Values +--- + +photoprism_docker_network_name: 'photoprism_network' diff --git a/roles/photoprism/handlers/main.yaml b/roles/photoprism/handlers/main.yaml new file mode 100644 index 0000000..cd21505 --- /dev/null +++ b/roles/photoprism/handlers/main.yaml @@ -0,0 +1,2 @@ +--- + diff --git a/roles/photoprism/tasks/main.yaml b/roles/photoprism/tasks/main.yaml new file mode 100644 index 0000000..73d807c --- /dev/null +++ b/roles/photoprism/tasks/main.yaml @@ -0,0 +1,52 @@ +# https://docs.photoprism.app/getting-started/docker-compose/ +--- + +- name: 'Create the photoprism network' + community.docker.docker_network: + name: '{{ photoprism_docker_network_name }}' + state: 'present' + +- name: 'Install Photoprism' + community.docker.docker_container: + name: 'photoprism' + image: 'photoprism/photoprism:latest' + pull: true + state: 'started' + recreate: false + security_opts: + - 'seccomp:unconfined' + - 'apparmor:unconfined' + env: + PHOTOPRISM_ADMIN_PASSWORD: "{{ photoprism_admin_password }}" # INITIAL PASSWORD FOR "admin" USER, MINIMUM 8 CHARACTERS + PHOTOPRISM_AUTH_MODE: "password" # authentication mode (public, password) + PHOTOPRISM_SITE_URL: "https://photos.kumardamani.net/" # public server URL incl http:// or https:// and /path, :port is optional + PHOTOPRISM_ORIGINALS_LIMIT: "5000" # file size limit for originals in MB (increase for high-res video) + PHOTOPRISM_HTTP_COMPRESSION: "gzip" # improves transfer speed and bandwidth utilization (none or gzip) + PHOTOPRISM_LOG_LEVEL: "info" # log level: trace, debug, info, warning, error, fatal, or panic + PHOTOPRISM_READONLY: "true" # do not modify originals directory (reduced functionality) + PHOTOPRISM_EXPERIMENTAL: "false" # enables experimental features + PHOTOPRISM_DISABLE_CHOWN: "true" # disables updating storage permissions via chmod and chown on startup + PHOTOPRISM_DISABLE_WEBDAV: "true" # disables built-in WebDAV server + PHOTOPRISM_DISABLE_SETTINGS: "false" # disables settings UI and API + PHOTOPRISM_DISABLE_TENSORFLOW: "true" # disables all features depending on TensorFlow + PHOTOPRISM_DISABLE_FACES: "true" # disables face detection and recognition (requires TensorFlow) + PHOTOPRISM_DISABLE_CLASSIFICATION: "true" # disables image classification (requires TensorFlow) + PHOTOPRISM_DISABLE_RAW: "true" # disables indexing and conversion of RAW files + PHOTOPRISM_RAW_PRESETS: "false" # enables applying user presets when converting RAW files (reduces performance) + PHOTOPRISM_JPEG_QUALITY: "85" # a higher value increases the quality and file size of JPEG images and thumbnails (25-100) + PHOTOPRISM_DETECT_NSFW: "false" # automatically flags photos as private that MAY be offensive (requires TensorFlow) + PHOTOPRISM_UPLOAD_NSFW: "false" # allows uploads that MAY be offensive (no effect without TensorFlow) + PHOTOPRISM_DATABASE_DRIVER: "sqlite" # SQLite is an embedded database that doesn't require a server + PHOTOPRISM_SITE_CAPTION: "Photos App" + PHOTOPRISM_SITE_DESCRIPTION: "Some of our pics" # meta site description + PHOTOPRISM_SITE_AUTHOR: "Kumar" # meta site author + PHOTOPRISM_INIT: "gpu tensorflow" + working_dir: "/photoprism" + networks: + - name: '{{ photoprism_docker_network_name }}' + ports: + - '{{ photoprism_host_port }}:2342' + volumes: + - '{{ photoprism_pics_root_dir }}:/photoprism/originals:ro' + - '{{ photoprism_data_dir }}:/photoprism/storage' + restart_policy: "unless-stopped" diff --git a/roles/plex/defaults/main.yaml b/roles/plex/defaults/main.yaml new file mode 100644 index 0000000..57e1fc7 --- /dev/null +++ b/roles/plex/defaults/main.yaml @@ -0,0 +1,4 @@ +# Default Values +--- + +plex_docker_network_name: 'plex_network' diff --git a/roles/plex/handlers/main.yaml b/roles/plex/handlers/main.yaml new file mode 100644 index 0000000..cd21505 --- /dev/null +++ b/roles/plex/handlers/main.yaml @@ -0,0 +1,2 @@ +--- + diff --git a/roles/plex/tasks/main.yaml b/roles/plex/tasks/main.yaml new file mode 100644 index 0000000..7cbb531 --- /dev/null +++ b/roles/plex/tasks/main.yaml @@ -0,0 +1,22 @@ +--- + +- name: 'Install Plex' + community.docker.docker_container: + name: 'plex' + image: 'lscr.io/linuxserver/plex:latest' + pull: true + state: 'started' + recreate: false + env: + PUID: "1001" + PGID: "1001" + VERSION: "docker" + PLEX_CLAIM: "{{ plex_claim }}" + network_mode: 'host' # will use 32400 + volumes: + - '{{ plex_library_dir }}:/config' + - '{{ plex_tv_dir }}:/tv' + - '{{ plex_mov_dir }}:/movies' + - '{{ plex_pic_dir }}:/pics' + - '{{ plex_music_dir }}:/music' + restart_policy: "unless-stopped" diff --git a/roles/transmission/tasks/main.yaml b/roles/transmission/tasks/main.yaml index 91d3971..229184f 100644 --- a/roles/transmission/tasks/main.yaml +++ b/roles/transmission/tasks/main.yaml @@ -14,8 +14,8 @@ state: 'started' recreate: true env: - "PUID": '1000' - "PGID": '1000' + "PUID": '1001' + "PGID": '1001' "TZ": 'America/Toronto' USER: '{{ transmission_user }}' PASS: '{{ transmission_password }}' diff --git a/roles/website/tasks/main.yaml b/roles/website/tasks/main.yaml index 66bc955..07780b5 100644 --- a/roles/website/tasks/main.yaml +++ b/roles/website/tasks/main.yaml @@ -2,11 +2,19 @@ - name: 'Install git' become: true - become_method: 'doas' + # become_method: 'doas' package: name: 'git' state: 'present' +- name: 'Remove domain level dir' + file: + path: '{{ sites_data_root }}/{{ item.name }}/html' + state: 'absent' + when: 'item.git_url is defined' + loop: '{{ websites }}' + + - name: 'Create domain level dir' file: path: '{{ sites_data_root }}/{{ item.name }}/html' @@ -33,3 +41,10 @@ remote_src: true when: 'item.git_url is defined' loop: '{{ websites }}' + +- name: 'Remove git checkout' + file: + path: '/home/{{ ansible_user }}/{{ item.name }}' + state: 'absent' + when: 'item.git_url is defined' + loop: '{{ websites }}' diff --git a/roles/wireguard/tasks/main.yaml b/roles/wireguard/tasks/main.yaml index c3f0942..aa8b20d 100644 --- a/roles/wireguard/tasks/main.yaml +++ b/roles/wireguard/tasks/main.yaml @@ -9,6 +9,7 @@ loop: - 'iptables' - 'wireguard-tools' + - 'libqrencode' - name: 'Enable iptables' service: |
