SSD Nodes Learn 🎉 VPS desde $4.99/mes
Guías Matt ConnorPor Matt Connor · Actualizado 2026-08-07

Cómo hacer backup y restaurar Immich en un VPS

Aprende qué debe incluir el backup de Immich, por qué copiar el directorio de datos de Postgres no basta y cómo evitar una cronología vacía al restaurar.

Qué debe contener una copia de seguridad de Immich

Una copia de seguridad de Immich consta de tres elementos capturados en el mismo momento. Los originales ubicados en UPLOAD_LOCATION. Un volcado SQL de la base de datos de Postgres. Los archivos .env y docker-compose.yml que describen la pila. Restaurar significa volver a cargar ese volcado en una base de datos nueva mientras el servidor de Immich está detenido, y arrancar el resto de la pila sólo después de hacerlo. Si se cambia el orden, puede terminar con un Immich operativo que muestra una cronología vacía sobre un disco lleno.

La separación es importante porque Immich mantiene su estado en dos ubicaciones que no tienen conocimiento mutuo. Postgres contiene cada álbum, cada grupo de rostros, cada enlace compartido, cada cuenta de usuario y clave de API, además de la ruta almacenada de cada recurso. El sistema de archivos contiene los píxeles. Si restaura los archivos sin la base de datos, Immich no muestra nada. Si restaura la base de datos sin los archivos, cada recurso se abre como una imagen dañada.

Los comandos de esta guía están escritos para Immich v3.1.0, la versión vigente a principios de agosto de 2026. El proyecto publica versiones con rapidez y el procedimiento de copia de seguridad documentado ha cambiado más de una vez, así que compruebe qué versión está ejecutando antes de copiar nada. Si la pila aún no está activa, empiece por la guía de instalación de Immich y vuelva aquí.

Conozca a qué apuntan las rutas

Dos variables de .env determinan todo lo que se explica en esta página. UPLOAD_LOCATION es el directorio principal en el que Immich escribe todos los archivos multimedia. DB_DATA_LOCATION es el directorio de datos de Postgres.

El example.env predeterminado establece UPLOAD_LOCATION=./library, un valor confuso porque Immich crea después una carpeta llamada library dentro de ese directorio. Los originales terminan en ./library/library. Establezca una ruta absoluta para que un script de copia de seguridad nunca dependa del directorio desde el que se ejecutó.

UPLOAD_LOCATION=/srv/immich/data
DB_DATA_LOCATION=/srv/immich/postgres
DB_USERNAME=postgres
DB_DATABASE_NAME=immich
IMMICH_VERSION=v3.1.0

Dentro de UPLOAD_LOCATION, Immich crea varias carpetas. Tres contienen datos que ningún trabajo puede reconstruir:

  • library: los originales, organizados según la plantilla de almacenamiento
  • upload: los originales que todavía no se han movido a la estructura de la plantilla, además de las cargas en curso
  • profile: las imágenes de perfil de los usuarios

Si pierde library, la fotografía desaparece. Immich no conserva ninguna segunda copia del original en otro lugar.

Por qué copiar el directorio de datos de Postgres no es una copia de seguridad

DB_DATA_LOCATION parece un objetivo sencillo. Es un directorio, rsync lo copiará y la copia termina sin errores. Aun así, no es una copia de seguridad por dos motivos que pueden provocar fallos.

El primero es la inconsistencia de la copia. Postgres escribe primero cada cambio en el registro de escritura anticipada (WAL) y después lo aplica a los archivos de las tablas durante un checkpoint. Por tanto, en cualquier momento los archivos del disco pueden estar en un estado intermedio. Una copia progresiva que tarda cuatro minutos lee el primer archivo a las 02:00 y el último a las 02:04. Esos dos archivos no pertenecen a la misma transacción. Cuando inicia Postgres con el resultado, rechaza el arranque con PANIC: could not locate a valid checkpoint record o inicia y se detiene al leer por primera vez una página dañada con invalid page in block 1234 of relation base/16384/.... Ninguna de las dos situaciones se puede recuperar a partir de esa copia.

El segundo motivo sigue siendo válido aunque detenga todo primero. Un directorio de datos de Postgres está ligado a los binarios exactos que lo escribieron. Immich fija su imagen de base de datos mediante un digest, actualmente ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0. Es Postgres 14 con dos extensiones de búsqueda vectorial compiladas. Un directorio de datos escrito por esa compilación no se abrirá con otra versión principal de Postgres ni con una compilación que incluya versiones diferentes de las extensiones. El host de restauración debe reproducir exactamente la imagen. Un volcado SQL no tiene esa limitación: es texto y cualquier servidor compatible puede reproducirlo.

pg_dump evita directamente el problema de la inconsistencia de la copia. Lee toda la base de datos dentro de una única instantánea MVCC (control de concurrencia multiversión), por lo que ve la base de datos exactamente como estaba en un instante concreto mientras continúan las demás escrituras. Por eso no es necesario detener Postgres para crear el volcado.

Qué se puede excluir de la copia de seguridad

Estos elementos se regeneran, por lo que puede omitirlos:

  • thumbs: imágenes de vista previa y miniaturas
  • encoded-video: vídeo transcodificado
  • DB_DATA_LOCATION: se vuelve a generar a partir del volcado
  • el volumen de Docker model-cache: modelos de machine learning, que se vuelven a descargar cuando se necesitan

Omitirlos es una decisión con costes; no es una ventaja gratuita. Volver a generar las miniaturas y transcodificar una biblioteca grande requiere horas de CPU en un VPS pequeño, y la línea de tiempo muestra marcadores de posición grises durante todo ese tiempo. Puede volver a ejecutarlos desde Administration > Jobs, con "Generate Thumbnails" y "Transcode Videos" configurados para ejecutarse cuando falten recursos. Si el destino de la copia de seguridad tiene espacio, inclúyalos y evite la espera. Si está cerca del límite de almacenamiento, omítalos y planifique la regeneración. Dimensionar una biblioteca de Immich explica cuánto crecen estas carpetas en relación con los originales.

Conviene conocer otra carpeta. UPLOAD_LOCATION/backups contiene los volcados automáticos de la base de datos de Immich, que se escriben cada día a las 02:00 y conservan los últimos 14. Esta configuración se puede ajustar en Administration > Settings > Backup. No le cuestan nada y son realmente útiles. También se encuentran en el mismo disco que la biblioteca que protegen, por lo que sirven para recuperarse de una migración defectuosa, pero no de un servidor averiado. Cree su propio volcado de todos modos, porque un volcado que active usted mismo se genera al mismo tiempo que la instantánea de archivos que le corresponde.

Hacer el volcado de la base de datos

docker exec -t immich_postgres pg_dump --clean --if-exists \
  --dbname=immich --username=postgres \
  | gzip > /srv/immich/backup/immich.sql.gz

Sustituya immich y postgres por DB_DATABASE_NAME y DB_USERNAME si los cambió. --clean --if-exists coloca DROP ... IF EXISTS delante de cada CREATE, de modo que el volcado se restaura en una base de datos que ya contiene objetos, en lugar de detenerse ante el primero.

Ahora, el detalle que puede arruinar silenciosamente los scripts de copia de seguridad. Ese comando es una tubería, y el shell informa del estado de salida del último comando de una tubería. Si pg_dump falla por una contraseña incorrecta o porque un contenedor no está en ejecución, gzip recibe un flujo vacío, escribe un archivo gzip perfectamente válido y termina con el código 0. El script registra que la operación se completó correctamente, pero la copia de seguridad ocupa 20 bytes. Coloque pipefail al principio de todos los scripts de copia de seguridad:

#!/usr/bin/env bash
set -euo pipefail

Después, compruebe el resultado en lugar de confiar en el código de salida:

ls -lh /srv/immich/backup/immich.sql.gz
gunzip -c /srv/immich/backup/immich.sql.gz | head -n 3

La primera línea de un volcado correcto es -- PostgreSQL database dump. Un archivo de unos cientos de bytes indica que el volcado falló, independientemente de lo que haya indicado el script.

Registre qué compilación lo generó junto al volcado:

docker inspect --format '{{.Config.Image}}' immich_server > /srv/immich/backup/immich-version.txt

No dependa de .env para esto. El archivo original establece IMMICH_VERSION=v3, una etiqueta flotante que sigue cada versión 3.x, por lo que no indica qué compilación generó realmente el volcado. Fije también la etiqueta exacta en .env.

Pausa el servidor y crea una instantánea con restic

Los archivos de UPLOAD_LOCATION no son inmutables mientras Immich está en ejecución. El servidor escribe las cargas nuevas y el trabajo de plantilla de almacenamiento mueve archivos entre directorios. Si una herramienta de copia de seguridad lee un archivo mientras se está escribiendo, guarda esos bytes como si fueran el archivo completo y no se informa de ningún error. Detén el contenedor del servidor durante toda la ejecución:

docker stop immich_server

Deja immich_postgres en ejecución porque el volcado lo necesita. La interfaz web y la aplicación móvil estarán fuera de línea hasta que vuelvas a iniciar el servidor. En una instancia doméstica, hacerlo a las 03:00 suele ser aceptable.

restic encaja aquí porque deduplica y cifra los datos antes de que salgan del servidor. Apúntalo a un repositorio que no esté en este servidor:

export RESTIC_REPOSITORY=sftp:backup@backup.example.com:/srv/restic/immich
export RESTIC_PASSWORD_FILE=/root/.restic-password
restic init

El almacenamiento de objetos funciona de la misma forma y es una opción mejor si quieres que la copia salga completamente de tu propio hardware:

export RESTIC_REPOSITORY=s3:https://s3.example.com/immich-backup
export AWS_ACCESS_KEY_ID=your-access-key
export AWS_SECRET_ACCESS_KEY=your-secret-key
restic init

Ese endpoint puede ser un bucket de MinIO que administres tú mismo en una segunda máquina o cualquier proveedor compatible con S3. Un repositorio en el mismo disco que la biblioteca te protege contra un borrado accidental, pero no contra ningún otro problema.

Después, crea la instantánea con una lista exacta de lo que importa:

restic backup \
  /srv/immich/backup/immich.sql.gz \
  /srv/immich/backup/immich-version.txt \
  /srv/immich/data/library \
  /srv/immich/data/upload \
  /srv/immich/data/profile \
  /srv/immich/.env \
  /srv/immich/docker-compose.yml
docker start immich_server

restic lee todo el árbol en cada ejecución, pero sólo carga los bloques que no ha visto antes. Por tanto, la primera instantánea transfiere toda la biblioteca y cada instantánea posterior transfiere las fotos nuevas del día.

Retención y claves que deben almacenarse en otro lugar

restic forget --prune --keep-daily 7 --keep-weekly 5 --keep-monthly 12

forget elimina las instantáneas del índice. --prune es la parte que elimina los datos de los que esas instantáneas eran la última referencia. Ejecute forget sin --prune y el coste del almacenamiento nunca disminuirá.

Las comprobaciones de estructura son económicas, así que ejecute una cada semana:

restic check

Esto verifica que los metadatos del repositorio sean coherentes. No lee sus datos. Una vez al mes, vuelva a leer una muestra y compárela con los hashes registrados:

restic check --read-data-subset=5%

Esta es la única comprobación que detecta la corrupción silenciosa en el backend de almacenamiento, porque descarga bloques reales y vuelve a calcular sus sumas de comprobación. Un --read-data completo en una biblioteca de fotos implica descargar todo el repositorio. En un almacenamiento de objetos con tarificación por uso, esto tiene un coste real, por lo que un subconjunto rotatorio es la opción que se utiliza en la práctica.

Ahora viene la parte que muchos omiten. La contraseña de un repositorio de restic no se puede recuperar. No existe ningún restablecimiento ni ticket de soporte. Si la única copia está en /root/.restic-password del servidor que intenta restaurar, sus copias de seguridad son datos cifrados inutilizables. Lo mismo se aplica a la clave de acceso al almacenamiento de objetos y a DB_PASSWORD de .env. Guárdelos en un lugar que no dependa de que esta máquina siga activa: impresos y guardados en un cajón, o en un gestor de contraseñas que se ejecute en otro hardware. Si ese gestor también está alojado por usted, necesita el mismo tratamiento, y hacer copias de seguridad de Vaultwarden es una tarea independiente.

Resta Immich en el orden correcto

El orden de restauración determina si una copia de seguridad conserva las cronologías o las deja vacías. Siga esta secuencia en el nuevo host.

Recupere primero la configuración. Indica qué versión debe ejecutar y a qué rutas apuntan los volúmenes.

restic restore latest --target /restore \
  --include /srv/immich/.env \
  --include /srv/immich/docker-compose.yml \
  --include /srv/immich/backup

Fije la versión antes de iniciar nada. Lea immich-version.txt, establezca IMMICH_VERSION en .env con esa etiqueta exacta y deje la versión más reciente para después. Immich no admite degradaciones, ni siquiera entre versiones de corrección. Si un servidor más reciente se inicia con un dump antiguo y ejecuta sus migraciones, no hay forma de volver atrás.

Restaure los archivos multimedia.

restic restore latest --target /restore --include /srv/immich/data

Después, mueva library, upload y profile para que queden directamente dentro de la ruta a la que apunta UPLOAD_LOCATION en este host. La ruta del host puede cambiar, porque el archivo compose enlaza ese directorio con una ruta fija dentro del contenedor. La estructura interna no puede cambiar.

Inicie sólo la base de datos. Deje DB_DATA_LOCATION vacío para que Postgres inicialice un clúster nuevo.

cd /srv/immich
docker compose pull
docker compose create
docker start immich_postgres
docker exec immich_postgres pg_isready --username=postgres

pg_isready muestra accepting connections cuando termina la configuración inicial. Tarda unos segundos. docker compose create crea todos los contenedores sin iniciarlos. Ese es el objetivo de este paso: el servidor de Immich todavía no debe ejecutarse. Un servidor que se inicia con una base de datos vacía ejecuta sus migraciones, crea un esquema nuevo y le pide que cree una cuenta de administrador. En ese momento estaría restaurando un dump debajo de una aplicación en ejecución.

Restaure el dump.

gunzip --stdout /restore/srv/immich/backup/immich.sql.gz \
  | sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
  | docker exec -i immich_postgres psql --dbname=immich --username=postgres \
      --single-transaction --set ON_ERROR_STOP=on

Dos elementos de ese comando son importantes. sed está presente porque pg_dump escribe un search_path vacío en su salida como medida de seguridad. Así, los nombres sin calificar del dump no pueden resolverse en un esquema inesperado. Los tipos de búsqueda vectorial de Immich están en public. Con una ruta de búsqueda vacía, la restauración llega a la primera columna declarada con un tipo vectorial y psql se detiene con ERROR: type "vector" does not exist. Volver a añadir public a la ruta de búsqueda corrige el problema.

--single-transaction --set ON_ERROR_STOP=on envuelve toda la restauración en una sola transacción que se cancela ante el primer error. El resultado es una base de datos completa o una base de datos sin cambios. Sin esta opción, un fallo a mitad del proceso deja una base de datos que se inicia y acepta el inicio de sesión, pero a la que le falta un número desconocido de álbumes. Puede descubrirlo semanas después.

Ahora inicie todo.

docker compose up -d
docker compose ps
docker logs -f immich_server

Espere una línea de inicio similar a Immich Server is listening on. Después, abra el puerto 2283 e inicie sesión con sus credenciales anteriores, porque las cuentas de usuario se restauraron junto con el dump. Si la página de inicio de sesión ofrece crear la primera cuenta de administrador, la base de datos no se restauró. Detenga el proceso y vuelva a leer la salida de psql.

Hay una advertencia sobre las instrucciones oficiales de restauración, que comienzan con docker compose down -v. -v elimina los volúmenes con nombre. En el archivo compose predeterminado, UPLOAD_LOCATION y DB_DATA_LOCATION son montajes bind, por lo que sobreviven. Si convirtió alguno de ellos en un volumen con nombre, ese comando elimina sus fotos. Lea el archivo compose antes de ejecutarlo.

Por qué la línea de tiempo está vacía después de una restauración

La línea de tiempo se genera a partir de filas de la base de datos. Immich nunca recorre upload/ durante el arranque para volver a descubrir las fotos, porque un archivo sin una fila no tiene propietario, fecha ni álbum. Por eso, la restauración incorrecta más habitual consiste en recuperar los archivos, pero no la base de datos. Immich se inicia, crea un esquema vacío y le entrega una instancia funcional sin contenido, aunque el disco esté lleno de fotos. No se ha perdido nada. Pero tampoco se muestra nada. La solución es volver a cargar el volcado con el servidor detenido, exactamente como se indicó arriba.

La segunda variante es más silenciosa. La base de datos se restaura, la línea de tiempo se llena de entradas y todos los recursos no se pueden abrir. Eso significa que las filas apuntan a archivos que el contenedor no puede ver. Normalmente ocurre porque library, upload y profile están un nivel demasiado abajo después de un restic restore --target /restore que nadie colocó en la ubicación correcta. Compruébelo desde dentro del contenedor en lugar de hacer suposiciones:

docker exec immich_server ls /data

El archivo compose estándar monta UPLOAD_LOCATION en /data, por lo que ese listado debería mostrar library, upload y profile. Si muestra un directorio vacío o una carpeta srv inesperada, el bind mount apunta al nivel incorrecto y las filas son correctas.

Compatibilidad de versiones entre la copia de seguridad y la restauración

Immich publica versiones con frecuencia y el esquema cambia junto con ellas. Por tanto, un volcado contiene el esquema de la versión del servidor que lo generó.

Restaurar un volcado antiguo en un servidor más reciente normalmente funciona, porque el servidor aplica sus migraciones pendientes al iniciar y actualiza el esquema de forma progresiva. Esta ruta se prueba a lo largo de la secuencia de versiones. El problema aparece al saltar varias versiones principales de una vez. El proyecto mantiene los cambios incompatibles en las versiones principales y los documenta en su registro de cambios.

Restaurar un volcado más reciente en un servidor antiguo no funciona. El volcado contiene tablas y columnas que el código antiguo no conoce. Immich indica que no admite la degradación, ni siquiera entre versiones de corrección. No existe un comando de reversión que pueda utilizar.

Por tanto, una restauración segura debe ser sencilla. Ejecute exactamente la versión que generó el volcado, restáurelo, inicie sesión y confirme que la línea temporal está completa. Actualice sólo después de hacerlo. Actualice una versión cada vez, incrementando IMMICH_VERSION y ejecutando docker compose pull && docker compose up -d después de cada actualización. Mantener una semana de volcados también ayuda: si el más reciente se creó durante una actualización fallida, el del día anterior seguirá disponible en el repositorio.

Verifique la copia de seguridad cada mes

Una copia de seguridad que nunca se ha restaurado es sólo una suposición. Una vez al mes, restáurela en una instancia desechable y revise una foto. La prueba tarda unos veinte minutos y es lo único que convierte el resto de esta página en un plan de recuperación.

restic snapshots
restic stats latest

snapshots debe mostrar la ejecución de anoche. stats latest debe indicar un tamaño cercano al de su biblioteca, no unos pocos megabytes.

Restaure la copia en un directorio temporal, preferiblemente en un host auxiliar:

restic restore latest --target /tmp/immich-drill

Copie docker-compose.yml y .env fuera del conjunto restaurado y cambie tres cosas en la copia. Haga que UPLOAD_LOCATION y DB_DATA_LOCATION apunten a directorios bajo /tmp/immich-drill. Publique el puerto web en otro lugar: 12283:2283 en lugar de 2283:2283. Elimine las líneas container_name:, porque el archivo compose incluido fija nombres como immich_server. Por eso, una segunda pila en el mismo host entra en conflicto con la primera y Docker rechaza su creación.

Ejecute la secuencia de restauración anterior: sólo la base de datos, reproduzca el volcado y, después, docker compose up -d. Ahora realice las cuatro comprobaciones que demuestran que la restauración funciona.

  1. Inicie sesión con la contraseña que usaba antes de la prueba. Si las cuentas funcionan, el volcado se restauró.
  2. Abra la línea de tiempo y desplácese hasta el mes más antiguo. Si hay recursos en todo el intervalo de fechas, se restauraron todas las filas, no sólo las recientes.
  3. Abra una foto a tamaño completo y descargue el original.
  4. Compárelo con el mismo archivo de su biblioteca activa mediante sha256sum. Si los hashes coinciden, los bytes sobrevivieron al proceso de ida y vuelta con restic.

Después, desmonte la prueba con docker compose down -v en el directorio de la prueba y elimine /tmp/immich-drill. Anote la fecha en un lugar visible, porque el valor de esta prueba depende por completo de repetirla el mes siguiente. Si todavía está decidiendo con qué servidor de fotos quedarse, la comparación entre PhotoPrism e Immich explica en qué se diferencian precisamente en este aspecto.

FAQ

¿Tengo que detener Immich para hacer una copia de seguridad?

Detenga immich_server y deje immich_postgres en ejecución. La base de datos no necesita una pausa, porque pg_dump lee dentro de una instantánea MVCC y ve un único instante coherente, independientemente de lo que esté escribiendo. Los archivos son el motivo para detenerlo: el servidor escribe nuevas cargas y el trabajo de plantillas de almacenamiento mueve archivos entre directorios. Por eso, una herramienta de copia de seguridad puede leer un archivo mientras se está escribiendo y guardar una copia truncada sin informar de ningún error. docker stop immich_server antes de la instantánea y docker start immich_server después de ella elimina esa condición de carrera.

¿Puedo copiar la carpeta de datos de Postgres en lugar de ejecutar pg_dump?

No. Una copia progresiva de un directorio de datos activo lee distintos archivos en instantes diferentes. El resultado no representa un estado coherente y Postgres lo rechaza al iniciar con PANIC: could not locate a valid checkpoint record, o falla más adelante al encontrar una página dañada. Incluso una copia realizada con todo detenido está vinculada a la compilación exacta de la base de datos: Immich fija una imagen de Postgres 14 con versiones específicas de las extensiones de búsqueda vectorial, y el directorio no se abrirá con ninguna otra. Un volcado SQL es texto sin formato y se puede restaurar en cualquier servidor compatible.

¿Por qué la línea de tiempo de Immich está vacía después de una restauración?

Porque la línea de tiempo se construye a partir de filas de la base de datos y restauró los archivos sin la base de datos. Immich nunca explora upload/ para redescubrir las fotos, por lo que los archivos que no tienen filas permanecen invisibles. Las fotos no se han modificado. Detenga el servidor, restaure el volcado en un Postgres recién inicializado y, después, inicie la pila. Si, en cambio, la línea de tiempo está completa pero no se puede abrir ninguna foto, el problema es el contrario: library, upload y profile no están directamente dentro del directorio enlazado al contenedor. Compruébelo con docker exec immich_server ls /data.

¿Qué carpetas de Immich puedo omitir en una copia de seguridad?

thumbs y encoded-video se regeneran a partir de los originales, y DB_DATA_LOCATION se reconstruye a partir del volcado. Por tanto, ninguna de ellas debe incluirse en el conjunto de copias de seguridad. Omitirlas consume tiempo después de una restauración en lugar de espacio antes de ella, ya que reconstruir las vistas previas y las transcodificaciones de una biblioteca grande requiere horas de CPU y se ejecuta desde Administration > Jobs para los recursos que faltan. Lo que nunca puede omitir es library, upload y profile, que contienen la única copia de cada original.

¿Puedo restaurar un volcado de Immich en una versión más reciente?

Normalmente sí, porque el servidor aplica las migraciones pendientes al iniciar y actualiza el esquema de forma progresiva. Lo contrario falla: Immich no admite volver a una versión anterior, ni siquiera entre versiones de corrección, por lo que un volcado de una versión más reciente no se puede cargar en un servidor antiguo. Restaure con IMMICH_VERSION fijado a la versión que generó el volcado, confirme que la línea de tiempo está completa y actualice después. Registre la versión junto a cada volcado con docker inspect --format '{{.Config.Image}}' immich_server, porque el valor predeterminado IMMICH_VERSION=v3 es una etiqueta variable que no proporciona ninguna información útil.