Add Youtarr stack and document PhotoSync storage

This commit is contained in:
Brian Pooe 2026-06-28 11:47:24 +02:00
parent d59814c8fe
commit 0fca695aa4
9 changed files with 244 additions and 33 deletions

View file

@ -28,6 +28,14 @@ LXC_IDMAP_BASE=100000
# Marker required at $DOCKERSTORAGEDIR/$ARR_DATA_MARKER before data-consuming services start.
ARR_DATA_MARKER=.arr-stack-storage
# Youtarr stack
# Host path for downloaded videos. Mount the youtube_downloads share here.
YOUTUBE_OUTPUT_DIR=/mnt/unas/youtube_downloads
# Published web UI port. The Youtarr container itself listens on port 3011.
YOUTARR_HOST_PORT=3087
# MariaDB root password for the bundled Youtarr database.
YOUTARR_DB_ROOT_PASSWORD=<strong-password>
# Home Assistant stack
# Static IP of the SLZB-06U Zigbee coordinator (used for TCP serial connection).
SLZB06_HOST=

View file

@ -1,6 +1,6 @@
# Homelab Blueprint Makefile
.PHONY: help check-env status deploy-caddy deploy-ha deploy-arr deploy-immich deploy-dns deploy-vault deploy-postgres deploy-gramps deploy-beszel deploy-proxy
.PHONY: help check-env status deploy-caddy deploy-ha deploy-arr deploy-immich deploy-dns deploy-vault deploy-postgres deploy-gramps deploy-beszel deploy-proxy deploy-youtarr
# Default shell
SHELL := /bin/bash
@ -26,6 +26,7 @@ help:
@echo " deploy-gramps - Deploy Gramps Web"
@echo " deploy-beszel - Deploy Beszel Agent"
@echo " deploy-proxy - Deploy Beszel Socket Proxy"
@echo " deploy-youtarr - Deploy Youtarr"
@echo ""
@echo "Utility Targets:"
@echo " status - Show status of all deployed stacks"
@ -131,6 +132,9 @@ deploy-beszel:
deploy-proxy:
$(call deploy_stack,socket-proxy,proxy)
deploy-youtarr:
$(call deploy_stack,youtarr,youtarr)
reload-caddy:
@docker compose -f docker-compose.caddy.yml ps --status running --services 2>/dev/null | grep -q '^caddy$$' \
|| { echo "Error: Caddy container is not running. Run 'make deploy-caddy' first." >&2; exit 1; }

View file

@ -31,6 +31,7 @@ Each stack guide in `docker-compose-files/<stack>/README.md` explains appdata pl
| [Immich](docker-compose-files/immich/README.md) | Photo and video management with GPU acceleration |
| [PostgreSQL](docker-compose-files/postgres/README.md) | PostgreSQL and pgAdmin |
| [Technitium DNS](docker-compose-files/technitium/README.md) | DNS server and ad blocking |
| [Youtarr](docker-compose-files/youtarr/README.md) | YouTube downloader with metadata and media-server integrations |
| [Vault](docker-compose-files/vault/README.md) | HashiCorp Vault secrets management |
## Management

View file

@ -0,0 +1,83 @@
# Youtarr
Docker deployment for Youtarr, a self-hosted YouTube downloader with metadata
for media servers. Run all commands from the repository root.
## Persistent Data
- `DOCKERCONFDIR/youtarr/config` stores Youtarr configuration.
- `DOCKERCONFDIR/youtarr/jobs` stores scheduled job state.
- `DOCKERCONFDIR/youtarr/server/images` stores thumbnails and cached images.
- `youtarr_db_data` stores the bundled MariaDB database.
- `YOUTUBE_OUTPUT_DIR` points at the shared video library, which should be
mounted to the UNAS `youtube_downloads` share.
## Installation
1. Create `.env` if it does not exist:
```bash
cp -n .env.sample .env
```
2. Set the required values in `.env`:
```dotenv
TZ=Africa/Johannesburg
PUID=1000
PGID=1000
DOCKERCONFDIR=/opt/appdata
YOUTUBE_OUTPUT_DIR=/mnt/unas/youtube_downloads
YOUTARR_HOST_PORT=3087
YOUTARR_DB_ROOT_PASSWORD=<strong-password>
DOCKERLOGGING_MAXFILE=3
DOCKERLOGGING_MAXSIZE=10m
```
3. Create the local directories:
```bash
mkdir -p /opt/appdata/youtarr/{config,jobs,server/images}
chown -R 1000:1000 /opt/appdata/youtarr
```
If this stack runs inside a Proxmox LXC, bind-mount
`/mnt/unas/youtube_downloads` into the container before starting Docker.
4. Render the template:
```bash
./substitute_env.sh docker-compose-files/youtarr/template.yaml docker-compose.youtarr.yml .env
```
5. Validate and start the stack:
```bash
docker compose -f docker-compose.youtarr.yml config --quiet
docker compose -f docker-compose.youtarr.yml up -d
```
6. Open Youtarr at `http://<HOST_IP>:3087`.
## Apply Configuration Changes
```bash
./substitute_env.sh docker-compose-files/youtarr/template.yaml docker-compose.youtarr.yml .env
docker compose -f docker-compose.youtarr.yml up -d
```
## Network Requirements
- `YOUTARR_HOST_PORT=3087` publishes the app on host port `3087`. The
container-side port remains `3011`, as required by the upstream image, so
the Compose mapping is `3087:3011` (`HOST:CONTAINER`).
- The video output directory must be mounted and writable before the stack
starts.
## Troubleshooting
- If MariaDB migration errors mention bind-mount corruption, keep the bundled
`youtarr_db_data` named volume instead of switching the database to a host
bind mount.
- If downloads do not appear, confirm `YOUTUBE_OUTPUT_DIR` points at the host
mount that backs the `youtube_downloads` share.

View file

@ -0,0 +1,94 @@
# Youtarr for Docker on a Proxmox LXC or host.
# The download directory is the shared youtube_downloads mount on the host.
services:
youtarr-db:
image: mariadb:10.3
container_name: youtarr-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: {{YOUTARR_DB_ROOT_PASSWORD}}
MYSQL_DATABASE: youtarr
MYSQL_TCP_PORT: 3321
MYSQL_CHARSET: utf8mb4
MYSQL_COLLATION: utf8mb4_unicode_ci
volumes:
- youtarr_db_data:/var/lib/mysql
command:
- --port=3321
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
- --innodb-file-per-table=1
- --innodb-large-prefix=ON
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-P", "3321", "-uroot", "-p{{YOUTARR_DB_ROOT_PASSWORD}}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
deploy:
resources:
limits:
memory: 1G
reservations:
memory: 256M
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-file: "{{DOCKERLOGGING_MAXFILE}}"
max-size: "{{DOCKERLOGGING_MAXSIZE}}"
youtarr:
image: dialmaster/youtarr:latest
container_name: youtarr
restart: unless-stopped
user: "{{PUID}}:{{PGID}}"
depends_on:
youtarr-db:
condition: service_healthy
environment:
IN_DOCKER_CONTAINER: 1
TZ: {{TZ}}
DB_HOST: youtarr-db
DB_PORT: 3321
DB_USER: root
DB_PASSWORD: {{YOUTARR_DB_ROOT_PASSWORD}}
DB_NAME: youtarr
YOUTUBE_OUTPUT_DIR: {{YOUTUBE_OUTPUT_DIR}}
ports:
# Upstream Youtarr listens on 3011 inside the container and defaults to
# publishing it as 3087 on the Docker host.
- "{{YOUTARR_HOST_PORT}}:3011"
volumes:
- {{YOUTUBE_OUTPUT_DIR}}:/usr/src/app/data
- {{DOCKERCONFDIR}}/youtarr/server/images:/app/server/images
- {{DOCKERCONFDIR}}/youtarr/config:/app/config
- {{DOCKERCONFDIR}}/youtarr/jobs:/app/jobs
healthcheck:
test: ["CMD", "curl", "--fail", "--silent", "--show-error", "--output", "/dev/null", "http://localhost:3011/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
deploy:
resources:
limits:
memory: 2G
reservations:
memory: 512M
security_opt:
- no-new-privileges:true
logging:
driver: json-file
options:
max-file: "{{DOCKERLOGGING_MAXFILE}}"
max-size: "{{DOCKERLOGGING_MAXSIZE}}"
networks:
default:
name: youtarr-network
volumes:
youtarr_db_data:

View file

@ -13,4 +13,5 @@ Current operational documentation:
- [Technitium deployment](../docker-compose-files/technitium/README.md): DNS and ad-blocking service setup.
- [Caddy deployment](../docker-compose-files/caddy/README.md): Reverse proxy with Cloudflare DNS-01.
- [Immich deployment](../docker-compose-files/immich/README.md): Self-hosted photo management with GPU acceleration.
- [Youtarr deployment](../docker-compose-files/youtarr/README.md): Self-hosted YouTube downloader with metadata generation.
- [UniFi custom firewall rules](network/firewall-rules.md): Reference list of custom firewall rules, target IPs, ports, and stateful return traffic configurations.

View file

@ -52,19 +52,10 @@ Phone ──(SMB)──▶ UNAS Pro (mobile_photos share)
Run these on the Proxmox host as `root`.
### 2a. Confirm the NFS export appeared
### 2a. Confirm the SMB share is available
```bash
showmount -e 10.0.10.25 | grep mobile
```
Expected output:
```
/volume/b69055de-0b30-4e8c-b7ef-b533d92733c3/.srv/.unifi-drive/mobile_photos/.data 10.0.10.10
```
If the line is missing, return to Part 1 and confirm NFS is enabled with the
Proxmox host IP in the allowed-hosts list.
`mobile_photos` is mounted on Proxmox over SMB/CIFS, using the shared host
credentials file at `/etc/unas-smb-credentials`.
### 2b. Create the mountpoint
@ -77,27 +68,23 @@ mkdir -p /mnt/unas/mobile_photos
Open `/etc/fstab` and add this line alongside the existing UNAS entries:
```fstab
10.0.10.25:/volume/b69055de-0b30-4e8c-b7ef-b533d92733c3/.srv/.unifi-drive/mobile_photos/.data /mnt/unas/mobile_photos nfs vers=3,noatime,nofail,_netdev,x-systemd.automount,x-systemd.mount-timeout=10s 0 0
//10.0.10.25/mobile_photos /mnt/unas/mobile_photos cifs credentials=/etc/unas-smb-credentials,vers=3.0,iocharset=utf8,ro,nofail,_netdev,x-systemd.automount,x-systemd.mount-timeout=10s 0 0
```
### 2d. Apply and verify
```bash
systemctl daemon-reload
umount -lf /mnt/unas/mobile_photos
mount -a
findmnt /mnt/unas/mobile_photos
```
Write test:
```bash
touch /mnt/unas/mobile_photos/.mount-test && rm /mnt/unas/mobile_photos/.mount-test && echo OK
ls -la /mnt/unas/mobile_photos | head -20
```
### 2e. Set ownership
```bash
chown -R 1000:1000 /mnt/unas/mobile_photos
```
No `chown` is needed here. The host mount is read-only, and Immich only
reads from it.
---
@ -241,6 +228,7 @@ folder pair; the paid version removes that limit.
- **Share**: `mobile_photos`
- **Username/Password**: your UNAS credentials
5. Test and save
- If the SMB connection fails from the `FAMILY` VLAN, add the UniFi policy `Allow FAMILY to UNAS` for TCP/445.
6. Back on the main screen, tap **Autotransfer** → enable it
7. Under **Autotransfer settings**:
- **Transfer**: **New photos and videos only** — this is the critical setting
@ -264,14 +252,14 @@ Update `docker-compose-files/immich/template.yaml` and
(`{{IMMICH_MOBILE_SYNC_DIR}}:/mnt/mobile:ro`) and the `IMMICH_MOBILE_SYNC_DIR`
env var alongside `IMMICH_EXTERNAL_LIBRARY`.
Also add `mobile_photos` to the share map in `docs/unas/nfs-mounts.md`.
Also add `mobile_photos` to the share map in `docs/unas/nfs-mounts.md` as the
SMB/CIFS exception.
---
## Verification Checklist
- [ ] `showmount -e 10.0.10.25 | grep mobile_photos` shows the export
- [ ] `findmnt /mnt/unas/mobile_photos` shows the mount on the Proxmox host
- [ ] `findmnt /mnt/unas/mobile_photos` shows a `cifs` mount on the Proxmox host
- [ ] `pct exec 102 -- docker exec immich_server ls /mnt/mobile` succeeds
- [ ] Immich library shows `/mnt/mobile` in its import paths
- [ ] Phone sync app completes a test run with no errors

View file

@ -28,6 +28,7 @@ This document serves as the single source of truth for the manual custom firewal
| `Allow Caddy to SLZ-06 Zigbee` | Allow | Both | TCP | `CADDY` | Any | `IoT` | `10.0.30.5` | `80` | **Yes** | Allows Caddy to proxy SLZB-06U Zigbee coordinator UI. |
| `Allow Caddy to Technitium` | Allow | Both | TCP | `CADDY` | Any | `TRUSTED` | `10.0.10.5` | `5380` | **Yes** | Allows Caddy to proxy Technitium DNS admin UI. |
| `Allow FAMILY to CADDY` | Allow | Both | TCP | `FAMILY` | Any | `CADDY` | Any | `443` | **Yes** | Allows FAMILY devices to connect to Caddy web proxy (HTTPS only). |
| `Allow FAMILY to UNAS` | Allow | Both | TCP | `FAMILY` | Any | `TRUSTED` | `10.0.10.25` | `445` | **Yes** | Allows FAMILY devices to connect to the UNAS Pro SMB share used by PhotoSync. |
| `Allow FAMILY to DNS` | Allow | Both | TCP/UDP | `FAMILY` | Any | `TRUSTED` | `10.0.10.5` | `53` | **Yes** | Allows FAMILY devices to query Technitium DNS. |
| `Allow GUEST to DNS` | Allow | Both | TCP/UDP | `GUEST` | Any | `TRUSTED` | `10.0.10.5` | `53` | **Yes** | Allows GUEST devices to query Technitium DNS. |
| `Allow IoT to DNS` | Allow | Both | TCP/UDP | `IoT` | Any | `TRUSTED` | `10.0.10.5` | `53` | **Yes** | Allows IoT devices to query Technitium DNS. |

View file

@ -1,7 +1,8 @@
# U-NAS Pro NFS Share Mounts on Proxmox
# U-NAS Pro Mounts on Proxmox
Persistent mounts for all six UniFi Drive shares on the Proxmox host, and how
to bind-mount each share into the LXC that consumes it.
Persistent mounts for the UniFi Drive shares on the Proxmox host, and how to
bind-mount each share into the LXC that consumes it. Most shares use NFS;
`mobile_photos` is the SMB/CIFS exception because PhotoSync writes to it.
## Understanding the U-NAS Path Structure
@ -33,10 +34,10 @@ b69055de-0b30-4e8c-b7ef-b533d92733c3
| `arr_data` | `/mnt/unas/arr_data` | arr-stack LXC |
| `gramps_data` | `/mnt/unas/gramps_data` | gramps-web LXC (mounted as `/mnt/gramps_media`) |
| `immich_data` | `/mnt/unas/immich_data` | immich LXC |
| `mobile_photos` | `/mnt/unas/mobile_photos` | immich LXC (phone → NAS sync target, mounted as `/mnt/mobile`) |
| `mobile_photos` | `/mnt/unas/mobile_photos` | immich LXC (PhotoSync SMB target; mounted read-only as `/mnt/mobile`) |
| `paperless_data` | `/mnt/unas/paperless_data` | paperless LXC |
| `pbs_backup` | `/mnt/unas/pbs_backup` | PBS datastore (host-level, no LXC bind) |
| `youtube_downloads` | `/mnt/unas/youtube_downloads` | arr-stack LXC |
| `youtube_downloads` | `/mnt/unas/youtube_downloads` | arr-stack LXC, Youtarr stack |
## 1. Enable NFS
@ -69,6 +70,10 @@ Repeat for every share in the list.
LXC containers do not mount NFS directly. The Proxmox host is the only NFS
client; LXCs get access via bind mounts from the host.
`mobile_photos` is the exception: it is mounted on the Proxmox host over
SMB/CIFS using `/etc/unas-smb-credentials`, then bind-mounted into the Immich
LXC.
> **Why No Root Squash for some shares?** UniFi Drive's default NFS mode
> squashes the client root to an anonymous UID (977:988). That anonymous user
> cannot `chown` files to UID 1000, so container services would be locked out.
@ -107,6 +112,9 @@ Export list for 10.0.10.25:
If a share is absent from the output, return to step 1 and confirm NFS is
enabled for that share with the Proxmox host IP in the allowed-hosts list.
`mobile_photos` will not appear in `showmount`; it is mounted separately over
SMB/CIFS.
## 4. Create Mountpoints
```bash
@ -114,6 +122,7 @@ mkdir -p \
/mnt/unas/arr_data \
/mnt/unas/gramps_data \
/mnt/unas/immich_data \
/mnt/unas/mobile_photos \
/mnt/unas/paperless_data \
/mnt/unas/pbs_backup \
/mnt/unas/youtube_downloads
@ -142,6 +151,12 @@ Add the following six lines. The UUID in the export path must match what
10.0.10.25:/volume/b69055de-0b30-4e8c-b7ef-b533d92733c3/.srv/.unifi-drive/youtube_downloads/.data /mnt/unas/youtube_downloads nfs vers=3,noatime,nofail,_netdev,x-systemd.automount,x-systemd.mount-timeout=10s 0 0
```
Add this separate CIFS line for the PhotoSync target:
```fstab
//10.0.10.25/mobile_photos /mnt/unas/mobile_photos cifs credentials=/etc/unas-smb-credentials,vers=3.0,iocharset=utf8,ro,nofail,_netdev,x-systemd.automount,x-systemd.mount-timeout=10s 0 0
```
## 6. Apply and Verify All Mounts
```bash
@ -157,9 +172,15 @@ for share in arr_data gramps_data immich_data paperless_data pbs_backup youtube_
done
```
All six must show a source line. A `MISSING` line means the share is not
mounted; check `/etc/fstab` for a typo in the export path or mountpoint, then
confirm the UNAS IP is reachable.
All six NFS shares must show a source line. A `MISSING` line means the share
is not mounted; check `/etc/fstab` for a typo in the export path or mountpoint,
then confirm the UNAS IP is reachable.
Confirm the SMB-backed `mobile_photos` mount separately:
```bash
findmnt /mnt/unas/mobile_photos
```
Quick write test for each share:
@ -170,6 +191,9 @@ for share in arr_data gramps_data immich_data paperless_data pbs_backup youtube_
done
```
`mobile_photos` is read-only on the Proxmox host, so no write test is needed
for that mount.
## 7. Bind Mounts into LXCs
Stop each LXC before adding mountpoints. Replace the VMID placeholders with
@ -199,6 +223,13 @@ pct start "$IMMICH_VMID"
The immich compose reads `IMMICH_UPLOAD_LOCATION=/mnt/unas/immich_data` from
the `.env` file. That path must match the `mp=` value above.
Add the SMB-backed `mobile_photos` mount to the same LXC so the Immich
container can read it as `/mnt/mobile`:
```bash
pct set "$IMMICH_VMID" -mp1 /mnt/unas/mobile_photos,mp=/mnt/unas/mobile_photos
```
### gramps-web LXC
The gramps-web compose binds `/mnt/gramps_media` from the LXC filesystem, so