feat: add docker-restore package (16-container restore script)

Pre-installs /root/restore-docker.sh in firmware for one-click
recovery of all Docker containers after reflash.
- Covers Jellyfin, Plex, Emby, Immich, Navidrome, n8n, etc.
- Auto-creates immich_default network if missing
- Includes Jellyfin WebRootPath fix and JS patch
- Placeholder passwords (edit before use)
This commit is contained in:
xiexiaobao 2026-06-26 10:28:31 +08:00
parent fdea857efe
commit aa53d5541d
3 changed files with 275 additions and 0 deletions

View File

@ -0,0 +1,43 @@
#
# Copyright (C) 2026 xiexiaobao
#
# This is free software, licensed under the GPL 2.0 license.
#
include $(TOPDIR)/rules.mk
PKG_NAME:=docker-restore
PKG_VERSION:=1.0
PKG_RELEASE:=1
PKG_MAINTAINER:=xiexiaobao
PKG_LICENSE:=GPL-2.0
include $(INCLUDE_DIR)/package.mk
define Package/docker-restore
SECTION:=utils
CATEGORY:=Utilities
TITLE:=Docker container restore script for R6C
DEPENDS:=+docker
PKGARCH:=all
endef
define Package/docker-restore/description
One-click restore script for all 16 Docker containers on NanoPi R6C.
Saves docker run commands for Jellyfin, Plex, Emby, Immich, Navidrome,
n8n, HomeAssistant, PhotoPrism, LANraragi, UptimeKuma, Aria2, etc.
endef
define Build/Configure
endef
define Build/Compile
endef
define Package/docker-restore/install
$(INSTALL_DIR) $(1)/root
$(INSTALL_BIN) ./files/restore-docker.sh $(1)/root/restore-docker.sh
endef
$(eval $(call BuildPackage,docker-restore))

View File

@ -0,0 +1,231 @@
#!/bin/sh
# R6C Docker Container Restore Script
# Pre-installed in firmware by docker-restore package
# Run after flashing iStoreOS: sh /root/restore-docker.sh
#
# EDIT THIS FILE: replace CHANGEME values with your actual passwords.
# If you preserved /mnt/usb1-0/, container data survives reflash.
# Immich DB password and Aria2 RPC secret can be found from your old install.
set -e
echo "=== R6C Docker Restore ==="
echo ""
# ---- Wait for dockerd ----
for i in 1 2 3 4 5 6 7 8 9 10 15 20; do
docker info >/dev/null 2>&1 && break
echo "Waiting for dockerd... (${i}s)"
sleep 3
done
docker info >/dev/null 2>&1 || { echo "dockerd not ready"; exit 1; }
echo "dockerd ready"
echo ""
# ---- Check Docker data root ----
CURRENT_ROOT=$(docker info --format "{{.DockerRootDir}}" 2>/dev/null)
if [ "$CURRENT_ROOT" = "/opt/docker" ] && [ -d "/mnt/usb1-0/docker" ]; then
echo "Docker data root is on eMMC but TF card has data."
echo "Run this to fix:"
echo ' uci set dockerd.globals.data_root=/mnt/usb1-0/docker'
echo ' uci add_list dockerd.globals.registry_mirrors=https://docker.1ms.run'
echo ' uci commit dockerd && /etc/init.d/dockerd restart'
echo ""
fi
restore_container() {
local name="$1"
shift
if docker inspect "$name" >/dev/null 2>&1; then
echo "SKIP: $name already exists"
return
fi
echo "Creating: $name"
docker run -d --name "$name" "$@"
echo "DONE: $name"
echo ""
}
# ========== Preparation ==========
# Create Immich network if missing
if ! docker network inspect immich_default >/dev/null 2>&1; then
echo "Creating network: immich_default"
docker network create immich_default
echo ""
fi
# ========== CONTAINERS ==========
# ---- n8n ----
restore_container n8n \
--restart=unless-stopped \
-e WEBHOOK_URL=https://n8n.maplemaple.top \
-e N8N_AI_ENABLED=true \
-e N8N_SECURE_COOKIE=false \
-v /opt/n8n/data:/home/node/.n8n \
-p 5678:5678 \
n8nio/n8n:latest
# ---- ChangeDetection ----
restore_container changedetection \
--restart=unless-stopped \
-e BASE_URL=https://watch.maplemaple.top \
-v /opt/changedetection/data:/datastore \
-p 127.0.0.1:5000:5000 \
ghcr.io/dgtlmoon/changedetection.io:latest
# ---- Jellyfin (with WebRootPath fix) ----
restore_container jellyfin \
--restart=unless-stopped \
--network=host \
-e TZ=Asia/Shanghai \
-v /mnt/usb1-0/Configs/Jellyfin:/config \
-v /mnt/usb1-0/Caches/Jellyfin:/config/transcodes \
-v /mnt/usb1-0:/mnt/usb1-0 \
--entrypoint sh \
jellyfin/jellyfin:2026062206-arm64 \
-c "ln -sf /jellyfin/jellyfin-web /jellyfin/wwwroot && exec /jellyfin/jellyfin"
# ---- Emby ----
restore_container emby \
--restart=unless-stopped \
--network=host \
-v /mnt/usb1-0/Configs/Emby:/config \
-v /mnt:/mnt \
emby/embyserver:latest
# ---- HomeAssistant ----
restore_container homeassistant \
--restart=unless-stopped \
--network=host \
-v /mnt/usb1-0/homeassistant:/config \
homeassistant/home-assistant:latest
# ---- Immich (4 containers) ----
# Note: Update DB_PASSWORD if your Immich DB password differs
IMMICH_DB_PASS="${IMMICH_DB_PASS:-CHANGEME_IMMICH_DB_PASSWORD}"
restore_container immich_postgres \
--restart=unless-stopped \
--network=immich_default \
-e POSTGRES_USER=postgres \
-e POSTGRES_DB=immich \
-e POSTGRES_PASSWORD="$IMMICH_DB_PASS" \
-e POSTGRES_INITDB_ARGS="--data-checksums" \
-v /opt/immich/postgres:/var/lib/postgresql/data:rw \
ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
restore_container immich_redis \
--restart=unless-stopped \
--network=immich_default \
docker.io/valkey/valkey:9
restore_container immich_server \
--restart=unless-stopped \
--network=immich_default \
-e TZ=Asia/Shanghai \
-e DB_USERNAME=postgres \
-e DB_DATABASE_NAME=immich \
-e DB_PASSWORD="$IMMICH_DB_PASS" \
-e UPLOAD_LOCATION=/mnt/usb1-0/Public/Immich \
-v /mnt/usb1-0/Public/Immich:/data:rw \
-p 2283:2283 \
ghcr.io/immich-app/immich-server:v2
restore_container immich_machine_learning \
--restart=unless-stopped \
--network=immich_default \
-e TZ=Asia/Shanghai \
-e DB_PASSWORD="$IMMICH_DB_PASS" \
-v immich_model-cache:/cache:rw \
ghcr.io/immich-app/immich-machine-learning:v2
# ---- UptimeKuma ----
restore_container uptime-kuma \
--restart=unless-stopped \
-v /mnt/usb1-0/uptime-kuma:/app/data \
-p 3001:3001 \
louislam/uptime-kuma:latest
# ---- Navidrome ----
restore_container navidrome \
--restart=unless-stopped \
-e ND_PORT=4533 \
-v /mnt/usb1-0/navidrome:/data \
-v /mnt/usb1-0/music:/music:ro \
-p 4533:4533 \
deluan/navidrome:latest
# ---- LANraragi ----
restore_container lanraragi \
--restart=unless-stopped \
-e TZ=Asia/Shanghai \
-v /mnt/usb1-0/Configs/LANraragi:/root/lanraragi/database \
-v /mnt/usb1-0/Public/Comics:/root/lanraragi/content \
-v /mnt:/mnt:rslave \
-p 3000:3000 \
dezhao/lanraragi_cn
# ---- PhotoPrism ----
restore_container photoprism \
--restart=unless-stopped \
-e PHOTOPRISM_UPLOAD_NSFW=true \
-e PHOTOPRISM_ADMIN_PASSWORD=CHANGEME_PHOTOPRISM_PASS \
-e TZ=Asia/Shanghai \
-v /mnt/usb1-0/Configs/PhotoPrism:/photoprism/storage \
-v /mnt/usb1-0/Public/Photo:/photoprism/originals \
-p 2342:2342 \
photoprism/photoprism
# ---- Plex ----
restore_container plex \
--restart=unless-stopped \
-e TZ=Asia/Shanghai \
-e PLEX_CLAIM= \
-v /mnt/usb1-0/Configs/Plex:/config \
-v /mnt/usb1-0/Caches/Plex:/transcode \
-v /mnt:/mnt:rslave \
-p 3005:3005 \
-p 32400:32400 \
-p 32410:32410/udp \
-p 32412:32412/udp \
-p 32413:32413/udp \
-p 32414:32414/udp \
-p 32469:32469 \
-p 8324:8324 \
linuxserver/plex:latest
# ---- Aria2 ----
# Update RPC_SECRET if yours differs
Aria2_RPC_SECRET="${ARIA2_SECRET:-CHANGEME_ARIA2_SECRET}"
restore_container aria2 \
--restart=unless-stopped \
-e PUID=0 -e PGID=0 \
-e RPC_SECRET="$Aria2_RPC_SECRET" \
-e TZ=Asia/Shanghai \
-v /opt/aria2/downloads:/downloads \
-v /opt/aria2/config:/config \
-p 6889:6888 \
-p 6889:6888/udp \
-p 6880:8080 \
p3terx/aria2-pro:latest
# ========== Jellyfin JS patch ==========
echo ""
echo "Patching Jellyfin web UI bug..."
for i in 1 2 3 4 5 6 7 8 9 10; do
docker exec jellyfin curl -s http://127.0.0.1:8096/system/info/public >/dev/null 2>&1 && break
sleep 3
done
docker exec jellyfin sh -c 'sed -i "s/key:\"getApi\",value:function(e){var t,n=this.getApiClient(e);return/key:\"getApi\",value:function(e){if(!e)return;var t,n=this.getApiClient(e);return/g" /wwwroot/main.jellyfin.bundle.js' 2>/dev/null || true
docker restart jellyfin 2>/dev/null || true
echo ""
echo "=== Restore complete ==="
echo ""
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
echo ""
echo "Next steps:"
echo " 1. Set PLEX_CLAIM (visit plex.tv/claim)"
echo " 2. Change PhotoPrism admin password in web UI"
echo " 3. Check Immich admin setup at http://192.168.100.1:2283"
echo " 4. Restart changedetection to pick up API key changes"

View File

@ -1 +1,2 @@
CONFIG_PACKAGE_jellyfin-autoinit=y
CONFIG_PACKAGE_docker-restore=y