mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
This adds initial cloud-init configuration files for the automatic installation of PhotoPrism on a Raspberry Pi. The files include metadata, network configuration, and an in-depth user-data script that installs Docker, configures paths, sets services, and prepares the system for PhotoPrism with Traefik and MariaDB.
This commit is contained in:
2
setup/photoprismpi/meta-data
Normal file
2
setup/photoprismpi/meta-data
Normal file
@@ -0,0 +1,2 @@
|
||||
instance-id: photoprism-pi-001
|
||||
local-hostname: photoprism-pi
|
||||
5
setup/photoprismpi/network-config
Normal file
5
setup/photoprismpi/network-config
Normal file
@@ -0,0 +1,5 @@
|
||||
version: 2
|
||||
ethernets:
|
||||
eth0:
|
||||
dhcp4: true
|
||||
optional: false
|
||||
405
setup/photoprismpi/user-data
Normal file
405
setup/photoprismpi/user-data
Normal file
@@ -0,0 +1,405 @@
|
||||
#cloud-config
|
||||
|
||||
# Set hostname
|
||||
hostname: photoprism-pi
|
||||
fqdn: photoprism-pi.local
|
||||
|
||||
# Update and upgrade packages
|
||||
package_update: true
|
||||
package_upgrade: true
|
||||
|
||||
# Install required packages
|
||||
packages:
|
||||
- apt-transport-https
|
||||
- ca-certificates
|
||||
- curl
|
||||
- gnupg
|
||||
- lsb-release
|
||||
- git
|
||||
- openssl
|
||||
- avahi-daemon
|
||||
|
||||
# Install Docker Engine (latest version)
|
||||
runcmd:
|
||||
# Print welcome message
|
||||
- printf "\e[0s;35m[SETUP] Starting PhotoPrism installation on Raspberry Pi...\e[0m\n"
|
||||
|
||||
# Uninstall old versions if they exist
|
||||
- printf "\e[0;35m[SETUP] Removing old Docker versions if present...\e[0m\n"
|
||||
- for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do apt-get remove -y $pkg || true; done
|
||||
- printf "\e[0;35m[SETUP] ✓ Old Docker versions removed or not present\e[0m\n"
|
||||
|
||||
# Add Docker's official GPG key
|
||||
- printf "\e[0;35m[SETUP] Adding Docker repository GPG key...\e[0m\n"
|
||||
- apt-get update
|
||||
- apt-get install -y ca-certificates curl
|
||||
- install -m 0755 -d /etc/apt/keyrings
|
||||
- curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
|
||||
- chmod a+r /etc/apt/keyrings/docker.asc
|
||||
- printf "\e[0;35m[SETUP] ✓ Docker GPG key added\e[0m\n"
|
||||
|
||||
# Add Docker repository
|
||||
- printf "\e[0;35m[SETUP] Adding Docker repository...\e[0m\n"
|
||||
- echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
- apt-get update
|
||||
- printf "\e[0;35m[SETUP] ✓ Docker repository added\e[0m\n"
|
||||
|
||||
# Install Docker Engine
|
||||
- printf "\e[0;35m[SETUP] Installing Docker Engine...\e[0m\n"
|
||||
- apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
- printf "\e[0;35m[SETUP] ✓ Docker Engine installed\e[0m\n"
|
||||
|
||||
# Create directories for PhotoPrism and Traefik
|
||||
- printf "\e[0;35m[SETUP] Creating directory structure for PhotoPrism...\e[0m\n"
|
||||
- mkdir -p /opt/photoprism/photos
|
||||
- mkdir -p /opt/photoprism/storage
|
||||
- mkdir -p /opt/photoprism/database
|
||||
- mkdir -p /opt/photoprism/import
|
||||
- mkdir -p /opt/photoprism/originals
|
||||
- mkdir -p /opt/photoprism/traefik/certs
|
||||
- mkdir -p /opt/photoprism/traefik/conf.d
|
||||
- mkdir -p /etc/traefik
|
||||
- chown -R 1000:1000 /opt/photoprism
|
||||
- chmod -R 755 /opt/photoprism
|
||||
- chmod -R ug+rwX,o-rwx /opt/photoprism/storage
|
||||
- chmod -R ug+rwX,o-rwx /opt/photoprism/database
|
||||
- chmod -R ug+rwX,o-rwx /opt/photoprism/import
|
||||
- chmod -R ug+rwX,o-rwx /opt/photoprism/originals
|
||||
- chmod -R ug+rwX,o-rwx /opt/photoprism/photos
|
||||
- printf "\e[0;35m[SETUP] ✓ PhotoPrism directory structure created\e[0m\n"
|
||||
|
||||
|
||||
# Create mount points for external drives
|
||||
- printf "\e[0;35m[SETUP] Creating mount points for external drives...\e[0m\n"
|
||||
- mkdir -p /mnt/{a,b,c,d}
|
||||
- chown -R 1000:1000 /mnt
|
||||
- printf "\e[0;35m[SETUP] ✓ External drive mount points created\e[0m\n"
|
||||
|
||||
# Configure external drives in fstab
|
||||
- printf "\e[0;35m[SETUP] Configuring external drives in fstab...\e[0m\n"
|
||||
- |
|
||||
cat >> /etc/fstab << 'EOF'
|
||||
/dev/sda1 /mnt/a auto nofail,noatime,noauto,x-systemd.automount,x-systemd.device-timeout=10s,uid=1000,gid=1000 0 0
|
||||
/dev/sdb1 /mnt/b auto nofail,noatime,noauto,x-systemd.automount,x-systemd.device-timeout=10s,uid=1000,gid=1000 0 0
|
||||
/dev/sdc1 /mnt/c auto nofail,noatime,noauto,x-systemd.automount,x-systemd.device-timeout=10s,uid=1000,gid=1000 0 0
|
||||
/dev/sdd1 /mnt/d auto nofail,noatime,noauto,x-systemd.automount,x-systemd.device-timeout=10s,uid=1000,gid=1000 0 0
|
||||
EOF
|
||||
- printf "\e[0;35m[SETUP] ✓ External drives configured in fstab\e[0m\n"
|
||||
|
||||
# Set up swap
|
||||
- printf "\e[0;35m[SETUP] Setting up swap space...\e[0m\n"
|
||||
- |
|
||||
cat > /usr/local/bin/swapon.sh << 'EOF'
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# add 8 GB of swap if no swap was configured yet
|
||||
if [[ -z $(swapon --show) ]]; then
|
||||
fallocate -l 8G /swapfile
|
||||
chmod 600 /swapfile
|
||||
mkswap /swapfile
|
||||
swapon /swapfile
|
||||
swapon --show
|
||||
free -h
|
||||
echo '/swapfile none swap sw 0 0' | tee -a /etc/fstab
|
||||
fi
|
||||
EOF
|
||||
- chmod +x /usr/local/bin/swapon.sh
|
||||
- /usr/local/bin/swapon.sh
|
||||
- printf "\e[0;35m[SETUP] ✓ Swap space configured\e[0m\n"
|
||||
|
||||
# Verify the installation is successful
|
||||
- printf "\e[0;35m[SETUP] Verifying Docker installation...\e[0m\n"
|
||||
- docker run --rm hello-world
|
||||
- printf "\e[0;35m[SETUP] ✓ Docker installation verified\e[0m\n"
|
||||
|
||||
# Ensure docker group exists and pi user is added to it
|
||||
- printf "\e[0;35m[SETUP] Ensuring pi user is in docker group...\e[0m\n"
|
||||
- getent group docker || groupadd docker
|
||||
- usermod -aG docker pi
|
||||
- printf "\e[0;35m[SETUP] ✓ Pi user added to docker group\e[0m\n"
|
||||
|
||||
# Enable services
|
||||
- printf "\e[0;35m[SETUP] Enabling required services...\e[0m\n"
|
||||
- systemctl enable docker
|
||||
- systemctl enable avahi-daemon
|
||||
- printf "\e[0;35m[SETUP] ✓ Services enabled\e[0m\n"
|
||||
|
||||
# Add a service to start PhotoPrism on boot
|
||||
- printf "\e[0;35m[SETUP] Creating PhotoPrism service...\e[0m\n"
|
||||
- |
|
||||
cat > /etc/systemd/system/photoprism.service << 'EOF'
|
||||
[Unit]
|
||||
Description=PhotoPrism Service
|
||||
After=docker.service network-online.target
|
||||
Requires=docker.service network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
WorkingDirectory=/opt/photoprism
|
||||
ExecStart=/usr/bin/docker compose up -d
|
||||
ExecStop=/usr/bin/docker compose down
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
- printf "\e[0;35m[SETUP] ✓ PhotoPrism service created\e[0m\n"
|
||||
|
||||
# Enable PhotoPrism service
|
||||
- printf "\e[0;35m[SETUP] Enabling and starting PhotoPrism service...\e[0m\n"
|
||||
- systemctl enable photoprism.service
|
||||
- systemctl start photoprism.service
|
||||
- printf "\e[0;35m[SETUP] ✓ PhotoPrism service started\e[0m\n"
|
||||
|
||||
# Configure Avahi for .local domain
|
||||
- printf "\e[0;35m[SETUP] Restarting and configuring Avahi for .local domain...\e[0m\n"
|
||||
- systemctl restart avahi-daemon
|
||||
- printf "\e[0;35m[SETUP] ✓ Avahi configured for .local domain\e[0m\n"
|
||||
|
||||
# Update MOTD and issue file with actual IP address
|
||||
- printf "\e[0;35m[SETUP] Updating MOTD and console login message with actual IP address...\e[0m\n"
|
||||
- chmod +x /usr/local/bin/update-motd-ip.sh
|
||||
- /usr/local/bin/update-motd-ip.sh
|
||||
- PRIMARY_IFACE=$(ip route | grep default | awk '{print $5}')
|
||||
- IP=$(ip addr show $PRIMARY_IFACE 2>/dev/null | grep -oP 'inet \K[\d.]+' || hostname -I | awk '{print $1}')
|
||||
- |
|
||||
cat > /etc/issue << EOF
|
||||
|
||||
|
||||
Welcome to PhotoPrism Pi!
|
||||
|
||||
You can access the web interface using the following URLs:
|
||||
http://$IP:2342/
|
||||
https://photoprism-pi.local/
|
||||
|
||||
For further information and help with troubleshooting:
|
||||
https://docs.photoprism.app/photoprism-pi/
|
||||
https://docs.photoprism.app/getting-started/troubleshooting/
|
||||
|
||||
EOF
|
||||
- printf "\e[0;35m[SETUP] ✓ MOTD and console login message updated with actual IP\e[0m\n"
|
||||
|
||||
|
||||
|
||||
# Write configuration files
|
||||
write_files:
|
||||
- path: /usr/local/bin/update-motd-ip.sh
|
||||
permissions: '0755'
|
||||
content: |
|
||||
#!/bin/bash
|
||||
IP=$(hostname -I | awk '{print $1}')
|
||||
sed -i "s|http://<your IP address>:2342/|http://$IP:2342/|g" /etc/motd
|
||||
|
||||
- path: /etc/motd
|
||||
permissions: '0644'
|
||||
content: |
|
||||
|
||||
Welcome to PhotoPrism Pi!
|
||||
|
||||
You can access the web interface using one of the
|
||||
following URLs:
|
||||
http://<your IP address>:2342/
|
||||
https://photoprism-pi.local/
|
||||
|
||||
Your initial password for the "admin" account is
|
||||
"photoprismpi". Please change it after logging in for the
|
||||
first time, especially if your device is connected to the
|
||||
Internet or a shared network.
|
||||
|
||||
For further information and help with troubleshooting:
|
||||
https://docs.photoprism.app/photoprism-pi/
|
||||
https://docs.photoprism.app/getting-started/troubleshooting/
|
||||
|
||||
- path: /opt/photoprism/compose.yaml
|
||||
permissions: '0644'
|
||||
content: |
|
||||
name: photoprism
|
||||
|
||||
services:
|
||||
photoprism:
|
||||
image: photoprism/photoprism:arm64
|
||||
depends_on:
|
||||
- mariadb
|
||||
restart: always
|
||||
security_opt:
|
||||
- seccomp=unconfined
|
||||
- apparmor=unconfined
|
||||
user: "1000:1000"
|
||||
ports:
|
||||
- "2342:2342"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.photoprism.rule=Host(`photoprism-pi.local`)"
|
||||
- "traefik.http.routers.photoprism.entrypoints=websecure"
|
||||
- "traefik.http.routers.photoprism.tls=true"
|
||||
- "traefik.http.services.photoprism.loadbalancer.server.port=2342"
|
||||
- "traefik.docker.network=photoprism"
|
||||
environment:
|
||||
PHOTOPRISM_ADMIN_PASSWORD: "photoprismpi"
|
||||
PHOTOPRISM_AUTH_MODE: "passwd"
|
||||
PHOTOPRISM_SITE_URL: "https://photoprism-pi.local/"
|
||||
PHOTOPRISM_SITE_CAPTION: "AI-Powered Photos App"
|
||||
PHOTOPRISM_ORIGINALS_LIMIT: 5000
|
||||
PHOTOPRISM_HTTP_COMPRESSION: "none"
|
||||
PHOTOPRISM_WORKERS: 2
|
||||
PHOTOPRISM_LOG_LEVEL: "info"
|
||||
PHOTOPRISM_READONLY: "false"
|
||||
PHOTOPRISM_EXPERIMENTAL: "false"
|
||||
PHOTOPRISM_DISABLE_CHOWN: "false"
|
||||
PHOTOPRISM_DISABLE_WEBDAV: "false"
|
||||
PHOTOPRISM_DISABLE_SETTINGS: "false"
|
||||
PHOTOPRISM_DISABLE_TENSORFLOW: "false"
|
||||
PHOTOPRISM_DISABLE_FACES: "false"
|
||||
PHOTOPRISM_DISABLE_CLASSIFICATION: "false"
|
||||
PHOTOPRISM_DISABLE_RAW: "false"
|
||||
PHOTOPRISM_RAW_PRESETS: "false"
|
||||
PHOTOPRISM_JPEG_QUALITY: 85
|
||||
PHOTOPRISM_DETECT_NSFW: "false"
|
||||
PHOTOPRISM_UPLOAD_NSFW: "true"
|
||||
PHOTOPRISM_DATABASE_DRIVER: "mysql"
|
||||
PHOTOPRISM_DATABASE_SERVER: "mariadb:3306"
|
||||
PHOTOPRISM_DATABASE_NAME: "photoprism"
|
||||
PHOTOPRISM_DATABASE_USER: "photoprism"
|
||||
PHOTOPRISM_DATABASE_PASSWORD: "insecure"
|
||||
PHOTOPRISM_INIT: "gpu"
|
||||
PHOTOPRISM_FFMPEG_ENCODER: "software"
|
||||
PHOTOPRISM_FFMPEG_BITRATE: "32"
|
||||
PHOTOPRISM_UID: 1000
|
||||
PHOTOPRISM_GID: 1000
|
||||
working_dir: "/photoprism"
|
||||
volumes:
|
||||
- "/opt/photoprism/storage:/photoprism/storage"
|
||||
- "/opt/photoprism/originals:/photoprism/originals"
|
||||
- "/mnt:/photoprism/originals/mnt:slave"
|
||||
- "/opt/photoprism/import:/photoprism/import"
|
||||
|
||||
mariadb:
|
||||
restart: always
|
||||
image: arm64v8/mariadb:10.10
|
||||
security_opt:
|
||||
- seccomp=unconfined
|
||||
- apparmor=unconfined
|
||||
command: mysqld --innodb-buffer-pool-size=256M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
|
||||
volumes:
|
||||
- "/opt/photoprism/database:/var/lib/mysql"
|
||||
environment:
|
||||
MARIADB_AUTO_UPGRADE: "1"
|
||||
MARIADB_INITDB_SKIP_TZINFO: "1"
|
||||
MARIADB_DATABASE: "photoprism"
|
||||
MARIADB_USER: "photoprism"
|
||||
MARIADB_PASSWORD: "insecure"
|
||||
MARIADB_ROOT_PASSWORD: "insecure"
|
||||
|
||||
traefik:
|
||||
restart: always
|
||||
image: traefik:v3.4
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
- "/opt/photoprism/traefik:/etc/traefik:rw"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
command:
|
||||
- "--api.insecure=false"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--entrypoints.web.address=:80"
|
||||
- "--entrypoints.websecure.address=:443"
|
||||
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
|
||||
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
|
||||
- "--providers.docker.network=photoprism"
|
||||
- "--providers.docker.defaultRule=Host(`{{ normalize .Name }}.local`)"
|
||||
- "--log.level=DEBUG"
|
||||
|
||||
watchtower:
|
||||
restart: always
|
||||
image: containrrr/watchtower
|
||||
environment:
|
||||
WATCHTOWER_CLEANUP: "true"
|
||||
WATCHTOWER_POLL_INTERVAL: 7200
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: photoprism
|
||||
driver: bridge
|
||||
|
||||
- path: /etc/avahi/avahi-daemon.conf
|
||||
permissions: '0644'
|
||||
content: |
|
||||
[server]
|
||||
#host-name=foo
|
||||
#domain-name=local
|
||||
#browse-domains=0pointer.de, zeroconf.org
|
||||
use-ipv4=yes
|
||||
use-ipv6=no
|
||||
allow-interfaces=eth0
|
||||
#deny-interfaces=eth1
|
||||
#check-response-ttl=no
|
||||
#use-iff-running=no
|
||||
#enable-dbus=yes
|
||||
#disallow-other-stacks=no
|
||||
#allow-point-to-point=no
|
||||
#cache-entries-max=4096
|
||||
#clients-max=4096
|
||||
#objects-per-client-max=1024
|
||||
#entries-per-entry-group-max=32
|
||||
ratelimit-interval-usec=1000000
|
||||
ratelimit-burst=1000
|
||||
|
||||
[wide-area]
|
||||
enable-wide-area=yes
|
||||
|
||||
[publish]
|
||||
#disable-publishing=no
|
||||
#disable-user-service-publishing=no
|
||||
#add-service-cookie=no
|
||||
#publish-addresses=yes
|
||||
publish-hinfo=no
|
||||
publish-workstation=no
|
||||
#publish-domain=yes
|
||||
#publish-dns-servers=192.168.50.1, 192.168.50.2
|
||||
#publish-resolv-conf-dns-servers=yes
|
||||
#publish-aaaa-on-ipv4=yes
|
||||
#publish-a-on-ipv6=no
|
||||
|
||||
[reflector]
|
||||
#enable-reflector=no
|
||||
#reflect-ipv=no
|
||||
#reflect-filters=_airplay._tcp.local,_raop._tcp.local
|
||||
|
||||
[rlimits]
|
||||
#rlimit-as=
|
||||
#rlimit-core=0
|
||||
#rlimit-data=8388608
|
||||
#rlimit-fsize=0
|
||||
#rlimit-nofile=768
|
||||
#rlimit-stack=8388608
|
||||
#rlimit-nproc=3
|
||||
|
||||
# Configure users
|
||||
users:
|
||||
- name: pi
|
||||
gecos: PhotoPrism User
|
||||
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||
groups: adm,dialout,cdrom,floppy,sudo,audio,dip,video,plugdev,netdev,docker
|
||||
shell: /bin/bash
|
||||
lock_passwd: false
|
||||
|
||||
# Set passwords
|
||||
chpasswd:
|
||||
expire: false
|
||||
list: |
|
||||
pi:raspberry
|
||||
|
||||
# Automatically reboot after cloud-init completes
|
||||
power_state:
|
||||
delay: "+1"
|
||||
mode: reboot
|
||||
message: "Rebooting system after cloud-init completes setup"
|
||||
timeout: 30
|
||||
condition: True
|
||||
|
||||
Reference in New Issue
Block a user