This commit is contained in:
Stefan Ostermann 2025-11-30 17:13:22 +00:00
parent 1241caa5c6
commit 46b36d504c
10 changed files with 318 additions and 134 deletions

View File

@ -1,6 +1,6 @@
services:
openwebui:
image: ghcr.io/open-webui/open-webui:0.6.23
image: ghcr.io/open-webui/open-webui:0.6.40
container_name: open-webui
restart: always
environment:

View File

@ -13,4 +13,4 @@ AUTHENTIK_EMAIL__USE_SSL=false
AUTHENTIK_EMAIL__TIMEOUT=10
# Email address authentik will send from, should have a correct @domain
AUTHENTIK_EMAIL__FROM=home@thoster.net
AUTHENTIK_TAG=2025.10.2

108
backup.sh
View File

@ -3,70 +3,90 @@ set -Eeuo pipefail
shopt -s inherit_errexit nullglob
# -----------------------------------------------------------------------------
# CONFIGURATION — adjust to taste
# CONFIGURATION
# -----------------------------------------------------------------------------
readonly RETENTION_DAYS=7
readonly BACKUP_BASE=/media/extension/backup/ubuntu
readonly MOUNT_POINT=/media/extension
# timestamp format — no spaces
#readonly NOW=$(date +'%Y-%m-%d_%H-%M-%S')
readonly NOW=$(date +'%Y-%m-%d')
readonly THIS_RUN=${BACKUP_BASE}/${NOW}
readonly LATEST=${BACKUP_BASE}/latest
readonly THIS_RUN="${BACKUP_BASE}/${NOW}"
readonly LATEST="${BACKUP_BASE}/latest"
# directories (or files) to back up:
# Directories (or files) to back up (without DB volumes)
declare -a SOURCE_ITEMS=(
"/home/oster/server"
"/home/oster/photo-lib"
# "/home/oster/photo-lib"
)
# rsync exclusions — add your own
# Rsync exclusions — exclude DB-related dirs that are dumped separately + common junk
declare -a EXCLUDES=(
--exclude='.cache'
--exclude='node_modules'
--exclude='*.tmp'
--exclude='**/postgresql'
--exclude='**/mysql'
--exclude='**/db'
--exclude='**/database'
)
# rsync niceties
# Rsync options
RSYNC_OPTS=(
-aAXHv # archive, preserve ACLs/xattrs, human-readable, verbose
-aAXHv # preserve everything, human-friendly, verbose
--delete # mirror deletions
--partial # keep partials if interrupted
--stats # give stats at end
--stats # summary
"${EXCLUDES[@]}"
)
# Dry-run? set to "--dry-run" to test without writing
DRY_RUN=""
# -----------------------------------------------------------------------------
# LOGGING
# -----------------------------------------------------------------------------
log() { echo "[$(date +'%F %T')] $*"; }
die() { log "FATAL: $*"; exit 1; }
# -----------------------------------------------------------------------------
# LOGGING & LOCKING / CLEANUP
# -----------------------------------------------------------------------------
log() { echo "[$(date +'%F %T')] $*"; }
die() { log "FATAL: $*"; exit 1; }
# Locking
lockfile="${BACKUP_BASE}/.backup.lock"
#trap 'rm -rf "$lockfile"' EXIT
#if ! touch "$lockfile" 2>/dev/null; then
# die "Another backup is running (lockfile exists)."
#fi
cleanup() { rm -f "$lockfile"; }
trap cleanup EXIT
if ! ( set -o noclobber; echo "$$" > "$lockfile" ) 2>/dev/null; then
die "Another backup is already running (lockfile exists at $lockfile)."
fi
# -----------------------------------------------------------------------------
# FUNCTIONS
# -----------------------------------------------------------------------------
check_mount() {
if ! mountpoint -q "$MOUNT_POINT"; then
die "Backup mountpoint $MOUNT_POINT is not mounted."
die "Backup mountpoint $MOUNT_POINT is NOT mounted."
fi
}
# Rotation: keep 7 daily and 4 weekly
rotate_old() {
log "Rotating backups older than ${RETENTION_DAYS} days."
find "$BACKUP_BASE" -maxdepth 1 -mindepth 1 -type d \
! -name 'latest' ! -name "$(basename "$THIS_RUN")" \
-mtime +${RETENTION_DAYS} \
-print -exec rm -rf {} \;
log "Applying retention policy (7 daily, 4 weekly)."
cd "$BACKUP_BASE" || die "Cannot cd into $BACKUP_BASE"
# find snapshot dirs YYYY-MM-DD only
for dir in $(ls -1 | grep -E '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'); do
keep=0
# Keep last 7 daily
for i in $(seq 0 6); do
[ "$dir" = "$(date -d "-$i day" +%F)" ] && keep=1
done
# Keep last 4 Sundays (weeklies)
for i in $(seq 0 27); do
d=$(date -d "last sunday -$i week" +%F)
[ "$dir" = "$d" ] && keep=1
done
if [ $keep -eq 0 ]; then
log " Deleting expired snapshot $dir"
rm -rf "$dir"
else
log " Keeping snapshot $dir"
fi
done
}
# -----------------------------------------------------------------------------
@ -75,33 +95,33 @@ rotate_old() {
log "=== Starting backup run: $NOW ==="
check_mount
mkdir -p "$BACKUP_BASE"
mkdir -p "$THIS_RUN"
mkdir -p "$BACKUP_BASE" "$THIS_RUN"
# Determine if we can do a hardlink-incremental snapshot
# Incremental snapshot?
if [ -d "$LATEST" ]; then
LINK_DEST="--link-dest=$(readlink -f $LATEST)"
log "Using linkdest for incremental snapshot → $LATEST"
LINK_DEST="--link-dest=$(readlink -f "$LATEST")"
log "Using link-dest for incremental snapshot → $LATEST"
else
LINK_DEST=""
log "No previous backup; doing full snapshot."
log "No previous backup; creating full snapshot."
fi
# Run backups
for src in "${SOURCE_ITEMS[@]}"; do
base=$(basename "$src")
dst="${THIS_RUN}/${base}"
mkdir -p "$dst"
log "Backing up $src$dst"
nice -n 10 ionice -c2 -n7 \
rsync "${RSYNC_OPTS[@]}" $LINK_DEST $DRY_RUN \
rsync "${RSYNC_OPTS[@]}" $LINK_DEST \
"$src"/ "$dst"/
done
# update the "latest" symlink atomically
tmp_latest="${BACKUP_BASE}/.latest.tmp"
rm -f "$tmp_latest"
ln -sfn "$(basename "$THIS_RUN")" "$tmp_latest"
mv -T "$tmp_latest" "$LATEST"
# Update latest symlink atomically
ln -sfn "$(basename "$THIS_RUN")" "$BACKUP_BASE/.latest.tmp"
mv -T "$BACKUP_BASE/.latest.tmp" "$LATEST"
log "Snapshots done. Running rotation policy..."
rotate_old
log "=== Backup completed successfully ==="

View File

@ -141,7 +141,7 @@
- at: 01:00:00
trigger: time
- trigger: time
at: 08:00:00
at: 08:30:00
conditions: []
actions:
- action: light.turn_off
@ -194,37 +194,7 @@
kelvin: 2627
target:
device_id: 4a993938cbf7289987eb684f71b00b92
- id: '1732736495026'
alias: Lichter aus bei Sonnenaufgang
description: ''
trigger:
- platform: time
at: 01:00:00
condition: []
action:
- action: light.turn_off
metadata: {}
data: {}
target:
device_id: 03477708398bb2e1ceb3f4a776770a29
mode: single
- id: '1732736623945'
alias: Licht an bei Sonnenuntergang
description: ''
trigger:
- platform: sun
event: sunset
offset: -00:30:00
condition: []
action:
- action: light.turn_on
metadata: {}
data:
kelvin: 2490
brightness_pct: 85
target:
device_id: 03477708398bb2e1ceb3f4a776770a29
mode: single
controller_device: 180e52c2b4e6bfd02de1a022cd77eda0
- id: '1733523441756'
alias: Küche Theke Schalter
description: ''
@ -297,11 +267,6 @@
device_id: a09f77bde7ba5098df0c3ec599ca7377
entity_id: 2a315e13dac7a4d0e3a5331216e79a5b
domain: switch
- action: light.turn_off
metadata: {}
data: {}
target:
device_id: aa992894922dfd99d4de351691fddbeb
mode: single
- id: '1733558486213'
alias: Küche Theke und Tisch aus
@ -569,3 +534,54 @@
target:
entity_id: counter.bewegung_kueche
mode: single
- id: '1761475605456'
alias: Bewegungsmelder Licht Treppe oben
description: ''
triggers:
- type: occupied
device_id: 4629e222cda722b7ad07de427acb56bd
entity_id: aae8135070bc723603a2477818b484a9
domain: binary_sensor
trigger: device
- type: occupied
device_id: d0addbba1dd8a92697554ff8a015345e
entity_id: 42c91c337a8f76b1559776a7daec6e84
domain: binary_sensor
trigger: device
conditions: []
actions:
- type: turn_on
device_id: 68bb158ff829c34611860fedff328e32
entity_id: 073c2fbfb9a0796461063e922049587e
domain: switch
- delay:
hours: 0
minutes: 10
seconds: 0
milliseconds: 0
- type: turn_off
device_id: 68bb158ff829c34611860fedff328e32
entity_id: 073c2fbfb9a0796461063e922049587e
domain: switch
mode: single
- id: '1761484676480'
alias: Treppenlicht aus verzögert
description: ''
triggers:
- type: turned_on
device_id: 68bb158ff829c34611860fedff328e32
entity_id: 073c2fbfb9a0796461063e922049587e
domain: switch
trigger: device
conditions: []
actions:
- delay:
hours: 0
minutes: 10
seconds: 0
milliseconds: 0
- type: turn_off
device_id: 68bb158ff829c34611860fedff328e32
entity_id: 073c2fbfb9a0796461063e922049587e
domain: switch
mode: single

View File

@ -14,8 +14,8 @@ shell_command: !include shell_commands.yaml
switch:
- platform: wake_on_lan
name: "mini PC"
host: "192.168.178.87"
mac: "58:47:CA:78:4E:59"
host: "192.168.178.138"
mac: "9c:6b:00:8b:3c:20"
turn_off:
service: shell_command.turn_off_remote_pc
- platform: template

View File

@ -51,8 +51,6 @@ devices:
friendly_name: Flurlicht
'0x7cb03eaa0a0176bd':
friendly_name: Küchentisch
'0x000d6f001417c142':
friendly_name: Fernbedienung 2
'0xa4c138e68023f4c6':
friendly_name: Wassersensor Keller
'0xa4c1389d42e94844':
@ -65,6 +63,12 @@ devices:
friendly_name: Wasser Sensor Bad
'0x000d6f00109eb214':
friendly_name: Fernbedienung
'0xa4c138f8d17c3daa':
friendly_name: Bewegungsmelder Treppe oben
'0xa4c138b00278ea51':
friendly_name: Bewegungsmelder Treppe oben 2
'0x000d6f001417c142':
friendly_name: Fernbedienung Kühlschrank
homeassistant:
enabled: true
version: 4

View File

@ -6,40 +6,40 @@
"installed_version": 51999504,
"latest_version": 51999504
},
"brightness": 105,
"state": "OFF",
"brightness": 254,
"state": "ON",
"color": {
"x": 0.4599,
"y": 0.4106
},
"color_temp": 370,
"linkquality": 90,
"linkquality": 72,
"update_available": false
},
"0x00212effff0cb8b6": {
"color_mode": "xy",
"state": "OFF",
"state_rgb": "OFF",
"brightness_white": 76,
"state_white": "OFF",
"brightness_white": 254,
"state_white": "ON",
"brightness_rgb": 26,
"color_mode_rgb": "color_temp",
"color_temp_rgb": 199,
"color_mode_white": "color_temp",
"color_temp_white": 284,
"color_temp_white": 365,
"color": {
"x": 0.6942,
"y": 0.2963
},
"color_white": {
"x": 0.4042,
"y": 0.3902
"x": 0.4566,
"y": 0.4098
},
"color_rgb": {
"x": 0.3445,
"y": 0.3512
},
"linkquality": 75,
"linkquality": 129,
"state_10": "OFF"
},
"0xa4c1384187f0960d": {
@ -51,20 +51,20 @@
"linkquality": 78
},
"0xa4c13864e6c8c400": {
"battery": 93,
"battery": 89,
"voltage": 2900,
"occupancy": false,
"tamper": false,
"battery_low": false,
"linkquality": 111
"linkquality": 60
},
"0xa4c138ef60e2fdbe": {
"contact": true,
"tamper": false,
"battery_low": false,
"voltage": 2800,
"battery": 100,
"linkquality": 150
"voltage": 2700,
"battery": 98,
"linkquality": 156
},
"0xf0d1b800001388f0": {
"update": {
@ -72,9 +72,9 @@
"installed_version": 17130496,
"latest_version": 17130496
},
"state": "OFF",
"state": "ON",
"brightness": 229,
"linkquality": 12,
"linkquality": 0,
"update_available": false
},
"0x7cb03eaa0a0176bd": {
@ -85,14 +85,9 @@
},
"brightness": 152,
"state": "ON",
"linkquality": 147,
"linkquality": 117,
"update_available": false
},
"0x000d6f001417c142": {
"voltage": 3000,
"battery": 100,
"linkquality": 66
},
"0xa4c138e68023f4c6": {
"battery": 59,
"water_leak": false,
@ -101,23 +96,23 @@
},
"0xa4c13838ba59a1b2": {
"battery": 100,
"temperature": 30.3,
"humidity": 39,
"temperature": 28.9,
"humidity": 42,
"temperature_unit": "celsius",
"temperature_calibration": 0,
"humidity_calibration": 0,
"linkquality": 144
"linkquality": 120
},
"0x000d6f001417c16e": {
"voltage": 3000,
"battery": 100,
"linkquality": 138
"linkquality": 117
},
"0xa4c1383ec7aa7d95": {
"temperature": 24.58,
"humidity": 52.71,
"battery": 59,
"linkquality": 150
"temperature": 23.45,
"humidity": 60.7,
"battery": 14,
"linkquality": 117
},
"0xa4c138066bf1f4c7": {
"battery": 100,
@ -134,12 +129,12 @@
"installed_version": 33574183,
"latest_version": 33574183
},
"linkquality": 90
"linkquality": 12
},
"0x001788010d1c884e": {
"brightness": 254,
"state": "ON",
"linkquality": 0,
"linkquality": 15,
"update": {
"state": "available",
"installed_version": 16786434,
@ -149,10 +144,10 @@
"color_mode": "color_temp"
},
"0xa4c1383db5531833": {
"temperature": 23.8,
"humidity": 43.3,
"battery": 29,
"linkquality": 147,
"temperature": 21.5,
"humidity": 84.2,
"battery": 25,
"linkquality": 15,
"update": {
"state": "idle",
"installed_version": 268513281,
@ -160,23 +155,45 @@
}
},
"0xa4c1389d42e94844": {
"temperature": 22.4,
"linkquality": 0,
"temperature": 21,
"linkquality": 126,
"battery": 100,
"humidity": 61,
"humidity": 53,
"temperature_unit": "fahrenheit",
"temperature_calibration": 0,
"humidity_calibration": 0
},
"0xa4c138570b1956d9": {
"temperature": 24.95,
"humidity": 51.97,
"battery": 46,
"linkquality": 72
"temperature": 18.63,
"humidity": 66.52,
"battery": 0,
"linkquality": 24
},
"0x000d6f00109eb214": {
"voltage": 3000,
"battery": 100,
"battery_low": false,
"linkquality": 138
},
"0xa4c138f8d17c3daa": {
"battery": 96,
"voltage": 2900,
"occupancy": false,
"tamper": false,
"battery_low": false,
"linkquality": 54
},
"0xa4c138b00278ea51": {
"battery": 86,
"occupancy": false,
"tamper": false,
"battery_low": false,
"voltage": 2900,
"linkquality": 24
},
"0x000d6f001417c142": {
"voltage": 3100,
"battery": 100,
"battery_low": false
"linkquality": 123
}
}

View File

@ -149,5 +149,133 @@
<Item1>96c156a2-7a13-4b3b-a8b8-fb80c94d20c0</Item1>
<Item2>RemoveDuplicatePlaylistChildren</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>9b354818-94d5-4b68-ac49-e35cb85f9d84</Item1>
<Item2>CreateNetworkConfiguration</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>a6dcacf4-c057-4ef9-80d3-61cef9ddb4f0</Item1>
<Item2>MigrateMusicBrainzTimeout</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>4fb5c950-1991-11ee-9b4b-0800200c9a66</Item1>
<Item2>MigrateNetworkConfiguration</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>a8e61960-7726-4450-8f3d-82c12daabbcb</Item1>
<Item2>MigrateEncodingOptions</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>4124c2cd-e939-4ffb-9be9-9b311c413638</Item1>
<Item2>DisableTranscodingThrottling</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>ef103419-8451-40d8-9f34-d1a8e93a1679</Item1>
<Item2>CreateLoggingConfigHeirarchy</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>3793eb59-bc8c-456c-8b9f-bd5a62a42978</Item1>
<Item2>MigrateActivityLogDatabase</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>acbe17b7-8435-4a83-8b64-6fcf162cb9bd</Item1>
<Item2>RemoveDuplicateExtras</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>5c4b82a2-f053-4009-bd05-b6fcad82f14c</Item1>
<Item2>MigrateUserDatabase</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>06387815-c3cc-421f-a888-fb5f9992bea8</Item1>
<Item2>MigrateDisplayPreferencesDatabase</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>a81f75e0-8f43-416f-a5e8-516ccab4d8cc</Item1>
<Item2>RemoveDownloadImagesInAdvance</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>5bd72f41-e6f3-4f60-90aa-09869abe0e22</Item1>
<Item2>MigrateAuthenticationDatabase</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>615dfa9e-2497-4dbb-a472-61938b752c5b</Item1>
<Item2>FixPlaylistOwner</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>d34bfc33-5d2e-4790-8085-069ef6eecb4e</Item1>
<Item2>MigrateRatingLevels</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>cf6fabc2-9fbe-4933-84a5-ffe52ef22a58</Item1>
<Item2>FixAudioData</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>96c156a2-7a13-4b3b-a8b8-fb80c94d20c0</Item1>
<Item2>RemoveDuplicatePlaylistChildren</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>9b354818-94d5-4b68-ac49-e35cb85f9d84</Item1>
<Item2>CreateNetworkConfiguration</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>a6dcacf4-c057-4ef9-80d3-61cef9ddb4f0</Item1>
<Item2>MigrateMusicBrainzTimeout</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>4fb5c950-1991-11ee-9b4b-0800200c9a66</Item1>
<Item2>MigrateNetworkConfiguration</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>a8e61960-7726-4450-8f3d-82c12daabbcb</Item1>
<Item2>MigrateEncodingOptions</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>4124c2cd-e939-4ffb-9be9-9b311c413638</Item1>
<Item2>DisableTranscodingThrottling</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>ef103419-8451-40d8-9f34-d1a8e93a1679</Item1>
<Item2>CreateLoggingConfigHeirarchy</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>3793eb59-bc8c-456c-8b9f-bd5a62a42978</Item1>
<Item2>MigrateActivityLogDatabase</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>acbe17b7-8435-4a83-8b64-6fcf162cb9bd</Item1>
<Item2>RemoveDuplicateExtras</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>5c4b82a2-f053-4009-bd05-b6fcad82f14c</Item1>
<Item2>MigrateUserDatabase</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>06387815-c3cc-421f-a888-fb5f9992bea8</Item1>
<Item2>MigrateDisplayPreferencesDatabase</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>a81f75e0-8f43-416f-a5e8-516ccab4d8cc</Item1>
<Item2>RemoveDownloadImagesInAdvance</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>5bd72f41-e6f3-4f60-90aa-09869abe0e22</Item1>
<Item2>MigrateAuthenticationDatabase</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>615dfa9e-2497-4dbb-a472-61938b752c5b</Item1>
<Item2>FixPlaylistOwner</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>d34bfc33-5d2e-4790-8085-069ef6eecb4e</Item1>
<Item2>MigrateRatingLevels</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>cf6fabc2-9fbe-4933-84a5-ffe52ef22a58</Item1>
<Item2>FixAudioData</Item2>
</ValueTupleOfGuidString>
<ValueTupleOfGuidString>
<Item1>96c156a2-7a13-4b3b-a8b8-fb80c94d20c0</Item1>
<Item2>RemoveDuplicatePlaylistChildren</Item2>
</ValueTupleOfGuidString>
</Applied>
</MigrationOptions>

View File

@ -1,6 +1,6 @@
services:
jellyfin:
image: jellyfin/jellyfin:10.10.6
image: jellyfin/jellyfin:10.10.7
container_name: jellyfin
user: 1000:1000
# network_mode: 'host'
@ -30,7 +30,7 @@ services:
restart: 'unless-stopped'
# Optional - alternative address used for autodiscovery
environment:
- JELLYFIN_PublishedServerUrl=http://example.com
- JELLYFIN_PublishedServerUrl=https://media.home.thoster.net
# Optional - may be necessary for docker healthcheck to pass if running in host network mode
extra_hosts:
- 'host.docker.internal:host-gateway'

View File

@ -1,4 +1,3 @@
version: "2.1"
services:
wikmd:
image: linbreux/wikmd:latest