mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 08:44:04 +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