Skip to content

PhotoPrism

AI-Powered Photo Management

PhotoPrism is an AI-powered photos app for the decentralized web. It uses the latest technologies to tag and find pictures automatically without getting in your way. Browse all your photos and videos without worrying about RAW conversion, duplicates, or video formats.

Why Choose PhotoPrism?

  • AI-powered - automatic tagging with facial recognition
  • Universal formats - RAW, HEIF/HEIC, and video support
  • Smart organization - world map and timeline views
  • Privacy-first - 100% private with no cloud dependencies

Install

Infrastructure as Code

This PhotoPrism instance is deployed using OpenTofu with custom Incus images. The infrastructure configuration manages container provisioning and persistent storage volumes.

The canonical configuration is maintained at Benoit/OpenTofu.

Custom PhotoPrism Image

The PhotoPrism Incus image is built and maintained at forgejo.benoit.jp.net/Benoit/Laminar.

Infrastructure Configuration

The OpenTofu configuration provisions:

  • Incus instance running custom PhotoPrism image
  • Four persistent storage volumes:
    • /opt/photoprism/config - PhotoPrism configuration
    • /opt/photoprism/storage - Database, cache, and sidecar files
    • /opt/photoprism/originals - Original photo files
    • /opt/photoprism/backup - Backup storage
OpenTofu Configuration
resource "incus_storage_volume" "photoprism_opt_photoprism_config" {
  name = "photoprism_opt_photoprism_config"
  pool = incus_storage_pool.default.name
  config = {
    "initial.gid"  = "1001"
    "initial.uid"  = "1001"
    "initial.mode" = "755"
  }
}

resource "incus_storage_volume" "photoprism_opt_photoprism_storage" {
  name = "photoprism_opt_photoprism_storage"
  pool = incus_storage_pool.default.name
  config = {
    "initial.gid"  = "1001"
    "initial.uid"  = "1001"
    "initial.mode" = "755"
  }
}

resource "incus_storage_volume" "photoprism_opt_photoprism_originals" {
  name = "photoprism_opt_photoprism_originals"
  pool = incus_storage_pool.default.name
  config = {
    "initial.gid"  = "1001"
    "initial.uid"  = "1001"
    "initial.mode" = "755"
  }
}

resource "incus_storage_volume" "photoprism_opt_photoprism_backup" {
  name = "photoprism_opt_photoprism_backup"
  pool = incus_storage_pool.default.name
}

resource "incus_instance" "photoprism" {
  name  = "photoprism"
  image = "laminar.incus:photoprism-1.2507.07+250707-d28b3101e-1benoitjpnet"

  device {
    name = "photoprism_opt_photoprism_config"
    type = "disk"
    properties = {
      path   = "/opt/photoprism/config"
      source = incus_storage_volume.photoprism_opt_photoprism_config.name
      pool   = incus_storage_pool.default.name
    }
  }

  device {
    name = "photoprism_opt_photoprism_storage"
    type = "disk"
    properties = {
      path   = "/opt/photoprism/storage"
      source = incus_storage_volume.photoprism_opt_photoprism_storage.name
      pool   = incus_storage_pool.default.name
    }
  }

  device {
    name = "photoprism_opt_photoprism_originals"
    type = "disk"
    properties = {
      path   = "/opt/photoprism/originals"
      source = incus_storage_volume.photoprism_opt_photoprism_originals.name
      pool   = incus_storage_pool.default.name
    }
  }

  device {
    name = "photoprism_opt_photoprism_backup"
    type = "disk"
    properties = {
      path   = "/opt/photoprism/backup"
      source = incus_storage_volume.photoprism_opt_photoprism_backup.name
      pool   = incus_storage_pool.default.name
    }
  }
}

Deploy Infrastructure

Apply OpenTofu configuration
tofu apply

After provisioning, configure PhotoPrism by editing /etc/photoprism/defaults.yml with your settings and preferences.

Upgrade

Incus Image Upgrade

When upgrading to a newer Incus image version, follow these steps to migrate your data.

Backup current instance data before upgrading:

Enter Incus container
incus shell photoprism

Backup configuration file

cp /etc/photoprism/defaults.yml /opt/photoprism/backup/

Backup MySQL databases

Dump all databases to preserve the photoprism data along with MariaDB user accounts and privileges:

mysqldump --all-databases > /opt/photoprism/backup/db.sql

Deploy to new Incus image and restore data:

Update OpenTofu configuration

Edit your OpenTofu configuration file to update the image version:

resource "incus_instance" "photoprism" {
  name  = "photoprism"
  image = "laminar.incus:photoprism-NEW-VERSION"  # Update this line
  # ...
}

Apply infrastructure changes

Provision new Incus container
tofu apply
Enter Incus container
incus shell photoprism

Restore configuration file

cp /opt/photoprism/backup/defaults.yml /etc/photoprism/

Restore MySQL databases

This restores the photoprism data and the MariaDB user accounts (including the database password from defaults.yml):

mysql < /opt/photoprism/backup/db.sql

Reload grant tables

A plain SQL restore does not reload the grant tables, so the restored user accounts will not authenticate until they are flushed:

Reload MariaDB privileges
mysql -e "FLUSH PRIVILEGES;"

Restart PhotoPrism service

systemctl restart photoprism

Upgrade Complete

Your PhotoPrism instance is now running on the new Incus image with all photos and AI data intact!


Related Documentation: - Infrastructure Overview - Complete self-hosting architecture - Docker Management Guide - Container orchestration tips