PhotoPrism es una aplicación web autoalojada para organizar fotos y vídeos: indexa tu biblioteca, lee los metadatos EXIF, clasifica las imágenes por contenido, reconoce caras y muestra las fotos en un mapa, todo en tu propio servidor. En este tutorial desplegarás PhotoPrism con Docker Compose y MariaDB en Ubuntu 24.04, indexarás una primera colección y lo publicarás en un dominio con HTTPS a través de Nginx.

Requisitos previos

  • Un servidor con Ubuntu 24.04 LTS con al menos 2 núcleos y 4 GB de RAM, por ejemplo un VPS de CubePath. La indexación de bibliotecas grandes y la clasificación por IA agradecen más núcleos y memoria.
  • Un usuario no root con privilegios sudo.
  • Docker Engine y el plugin de Docker Compose instalados desde el repositorio oficial de Docker.
  • Espacio en disco para tus fotos más un 20 a 30 % adicional para miniaturas y caché.
  • Un dominio con un registro A apuntando a la IP del servidor, por ejemplo fotos.tu_dominio.

Paso 1: Preparar los directorios

PhotoPrism trabaja con dos carpetas: originals, donde viven tus fotos originales, y storage, donde guarda miniaturas, caché, sidecars y copias de seguridad del índice. La base de datos de MariaDB irá en una tercera carpeta:

mkdir -p ~/photoprism/{originals,storage,database}
cd ~/photoprism

Genera dos contraseñas, una para el administrador de PhotoPrism y otra para la base de datos. Anótalas, las usarás en el siguiente paso:

openssl rand -base64 18
openssl rand -hex 16

La contraseña de administrador debe tener al menos 8 caracteres. Para la de la base de datos se usa hexadecimal para evitar caracteres especiales en el fichero YAML.

Paso 2: Crear el fichero de Docker Compose

Crea el fichero compose.yaml basado en la configuración oficial, reducido a las opciones que realmente necesitas:

nano ~/photoprism/compose.yaml
services:
  photoprism:
    image: photoprism/photoprism:latest
    restart: unless-stopped
    stop_grace_period: 15s
    depends_on:
      - mariadb
    security_opt:
      - seccomp:unconfined
      - apparmor:unconfined
    ports:
      - "127.0.0.1:2342:2342"
    environment:
      PHOTOPRISM_ADMIN_USER: "admin"
      PHOTOPRISM_ADMIN_PASSWORD: "contraseña_admin"
      PHOTOPRISM_AUTH_MODE: "password"
      PHOTOPRISM_SITE_URL: "https://fotos.tu_dominio/"
      PHOTOPRISM_DISABLE_TLS: "true"
      PHOTOPRISM_ORIGINALS_LIMIT: 5000
      PHOTOPRISM_HTTP_COMPRESSION: "gzip"
      PHOTOPRISM_LOG_LEVEL: "info"
      PHOTOPRISM_DATABASE_DRIVER: "mysql"
      PHOTOPRISM_DATABASE_SERVER: "mariadb:3306"
      PHOTOPRISM_DATABASE_NAME: "photoprism"
      PHOTOPRISM_DATABASE_USER: "photoprism"
      PHOTOPRISM_DATABASE_PASSWORD: "contraseña_bd"
      PHOTOPRISM_SITE_CAPTION: "Mis fotos"
    working_dir: "/photoprism"
    volumes:
      - "./originals:/photoprism/originals"
      - "./storage:/photoprism/storage"

  mariadb:
    image: mariadb:11
    restart: unless-stopped
    stop_grace_period: 15s
    security_opt:
      - seccomp:unconfined
      - apparmor:unconfined
    command: --innodb-buffer-pool-size=512M --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:
      - "./database:/var/lib/mysql"
    environment:
      MARIADB_AUTO_UPGRADE: "1"
      MARIADB_INITDB_SKIP_TZINFO: "1"
      MARIADB_DATABASE: "photoprism"
      MARIADB_USER: "photoprism"
      MARIADB_PASSWORD: "contraseña_bd"
      MARIADB_ROOT_PASSWORD: "contraseña_root_bd"

Sustituye contraseña_admin, contraseña_bd (debe ser la misma en los dos servicios), contraseña_root_bd y fotos.tu_dominio por tus valores. Algunas decisiones importantes:

  • El puerto se publica solo en 127.0.0.1. Docker añade sus propias reglas de iptables y un puerto publicado en todas las interfaces quedaría expuesto a internet aunque UFW lo bloquee. Nginx será la única entrada.
  • PHOTOPRISM_DISABLE_TLS desactiva el certificado autofirmado que PhotoPrism generaría al ver una URL https://, porque de TLS se encargará Nginx.
  • PHOTOPRISM_ADMIN_PASSWORD solo se usa al crear la cuenta la primera vez. Para cambiarla después usa la interfaz web o photoprism passwd.

Como el fichero contiene contraseñas, restringe sus permisos:

chmod 600 ~/photoprism/compose.yaml

Paso 3: Arrancar PhotoPrism

Descarga las imágenes y arranca los contenedores en segundo plano:

cd ~/photoprism
docker compose up -d

El primer arranque tarda un par de minutos mientras MariaDB inicializa la base de datos y PhotoPrism crea las tablas. Comprueba el estado:

docker compose ps
NAME                     IMAGE                          SERVICE      STATUS                    PORTS
photoprism-mariadb-1     mariadb:11                     mariadb      Up 2 minutes
photoprism-photoprism-1  photoprism/photoprism:latest   photoprism   Up 2 minutes              127.0.0.1:2342->2342/tcp

Verifica que la aplicación responde en local:

curl -s -o /dev/null -w "%{http_code}\n" -L http://127.0.0.1:2342/
200

Si el contenedor se reinicia en bucle, revisa docker compose logs photoprism --tail 50; lo más habitual es una contraseña de base de datos que no coincide entre los dos servicios.

Paso 4: Publicar PhotoPrism por HTTPS con Nginx

Instala Nginx y Certbot, y permite el tráfico web en UFW:

sudo apt install nginx certbot python3-certbot-nginx
sudo ufw allow OpenSSH
sudo ufw allow "Nginx Full"
sudo ufw enable

Crea el sitio:

sudo nano /etc/nginx/sites-available/photoprism
server {
    listen 80;
    listen [::]:80;
    server_name fotos.tu_dominio;

    # Permite subir fotos y vídeos grandes desde el navegador
    client_max_body_size 500M;

    location / {
        proxy_pass http://127.0.0.1:2342;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket para las notificaciones en tiempo real
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_buffering off;
        proxy_read_timeout 600s;
    }
}

Activa el sitio, valida la configuración y obtén el certificado:

sudo ln -s /etc/nginx/sites-available/photoprism /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d fotos.tu_dominio

Abre https://fotos.tu_dominio e inicia sesión con el usuario admin y la contraseña que definiste. En Settings puedes cambiar el idioma de la interfaz a español.

Paso 5: Añadir e indexar fotos

PhotoPrism no vigila la carpeta de originales en tiempo real: los ficheros nuevos aparecen cuando se indexan. Copia tus fotos a ~/photoprism/originals, respetando la estructura de carpetas que quieras conservar. Desde otro equipo puedes usar rsync:

rsync -avh --progress /ruta/local/fotos/ tu_usuario@ip_del_servidor:~/photoprism/originals/

Lanza la indexación desde la línea de comandos. Es más rápido y más fácil de seguir que desde la web en bibliotecas grandes:

cd ~/photoprism
docker compose exec photoprism photoprism index --cleanup

La opción --cleanup elimina del índice los ficheros que ya no existen y las miniaturas huérfanas. El comando muestra cada fichero procesado y termina con un resumen del número de ficheros indexados y el tiempo empleado.

También puedes indexar desde la interfaz en Library > Index. Si prefieres subir fotos desde el navegador o la app móvil (por WebDAV), PhotoPrism las guarda en originals y las indexa automáticamente.

Para que las fotos que copias por rsync se indexen sin intervención, programa el índice con cron. Abre tu crontab:

crontab -e

Añade esta línea para indexar cada noche a las 3:00:

0 3 * * * cd "$HOME/photoprism" && docker compose exec -T photoprism photoprism index --cleanup > /dev/null 2>&1

Paso 6: Reconocimiento facial y búsqueda

Durante la indexación, PhotoPrism clasifica cada imagen (playa, montaña, perro, documento...) y detecta caras. Las caras que aparecen varias veces se agrupan automáticamente en People; asigna un nombre a cada grupo y PhotoPrism reconocerá a esa persona en el resto de la biblioteca.

Consulta el estado del reconocimiento facial:

docker compose exec photoprism photoprism faces stats

Si después de nombrar personas quieres que PhotoPrism vuelva a agrupar y asignar caras en toda la biblioteca, ejecuta:

docker compose exec photoprism photoprism faces index

Las fotos con coordenadas GPS en el EXIF se muestran en Places. En el buscador puedes combinar filtros, por ejemplo label:beach year:2024 o country:es.

Paso 7: Copias de seguridad

Tus fotos originales son lo más importante, pero el índice (álbumes, personas, etiquetas, favoritos) vive en la base de datos. PhotoPrism puede volcar la base de datos y los álbumes en storage/backup:

docker compose exec photoprism photoprism backup -i -a -f

Comprueba que se ha generado el volcado:

ls -lhR ~/photoprism/storage/backup/

Copia regularmente fuera del servidor, con rsync, restic o borg, las carpetas originals y storage/backup. Las miniaturas y la caché de storage se pueden regenerar y no hace falta incluirlas.

Actualizar PhotoPrism

Haz primero una copia de seguridad (paso 7) y después descarga las nuevas imágenes y recrea los contenedores:

cd ~/photoprism
docker compose pull
docker compose up -d

MariaDB actualizará sus tablas automáticamente gracias a MARIADB_AUTO_UPGRADE. Revisa docker compose logs photoprism --tail 30 para confirmar que arranca sin errores.

Solución de problemas

Access denied for user 'photoprism' en los logs. La contraseña de PHOTOPRISM_DATABASE_PASSWORD y MARIADB_PASSWORD no coinciden, o las cambiaste después del primer arranque. MariaDB solo lee esas variables al crear la base de datos; si ya existía, cambia la contraseña dentro de MariaDB o, en una instalación nueva, vacía la carpeta database y vuelve a arrancar.

Las fotos nuevas no aparecen. PhotoPrism no indexa originals en tiempo real. Ejecuta photoprism index o revisa que el cron del paso 5 está instalado con crontab -l.

La indexación satura el servidor. Limita los procesos en paralelo añadiendo PHOTOPRISM_WORKERS: 2 en environment y ejecuta docker compose up -d para aplicarlo.

Error 413 al subir vídeos desde el navegador. El vídeo supera client_max_body_size de Nginx. Aumenta el valor en el sitio y recarga con sudo systemctl reload nginx.

Conclusión

Has desplegado PhotoPrism con MariaDB en Docker, protegido detrás de Nginx con HTTPS, con indexación programada y copias del índice. Como siguientes pasos puedes conectar la app móvil PhotoSync u otro cliente WebDAV para subir fotos automáticamente, crear usuarios adicionales con photoprism users add o montar un volumen de almacenamiento dedicado para originals cuando la biblioteca crezca.