Immich autohospedado: 6 GB de RAM y upgrades seguros
La cifra real de RAM, el puerto 2283 tras HTTPS, el error exit 137 por falta de memoria y por qué Immich v3 no inicia con pgvecto.rs. Incluye restauración.
Qué va a implementar
Immich es un servicio autogestionado para realizar copias de seguridad de fotos y vídeos, una alternativa real a Google Photos. Incluye una aplicación para el teléfono que carga el carrete de la cámara en segundo plano, una línea de tiempo, álbumes, reconocimiento facial y búsquedas basadas en aprendizaje automático que encuentran «playa» o a una persona sin que tenga que etiquetar nada. Se ejecuta en un VPS de su propiedad, los archivos originales permanecen en su disco y nadie los analiza para venderle productos. Si todavía está comparándolo con la otra opción evidente, nuestra comparación entre PhotoPrism e Immich muestra lado a lado sus requisitos mínimos de RAM, aplicaciones para el teléfono y comandos de copia de seguridad.
La instalación consta de cuatro contenedores definidos en el archivo Docker Compose del propio proyecto. Esa parte tarda diez minutos. El resto de esta guía es donde aparecen los problemas: el contenedor de aprendizaje automático consume mucha memoria en un equipo pequeño, los originales llenan el disco rápidamente, la aplicación móvil rechaza un servidor que use HTTP sin cifrar e Immich publica cambios incompatibles con suficiente frecuencia como para que un docker compose pull descuidado pueda impedir que la base de datos se inicie. Tome en serio esos cuatro aspectos y Immich será muy estable. Si los ignora, perderá todo un fin de semana.
Requisitos previos y advertencias importantes
- RAM: la documentación oficial indica 6 GB como mínimo y 8 GB como recomendado; considere 4 GB más swap como el mínimo absoluto. Los contenedores
immich-servery Postgres consumen pocos recursos. El contenedorimmich-machine-learningconsume muchos recursos: carga modelos de CLIP y reconocimiento facial en la RAM para crear índices de búsqueda, y en un servidor con 2 GB el kernel lo termina. Añada swap aunque tenga 4 GB. - Disco: calcule el tamaño para toda la biblioteca y deje margen. Los archivos originales se copian completos. Además, Immich genera miniaturas e imágenes de vista previa, aproximadamente un 10–20% adicional. Una colección de fotos de 200 GB necesita un volumen de 300 GB. Postgres ocupa poco en comparación.
- CPU: cualquier VPS KVM moderno es suficiente, pero el procesamiento de ML en la CPU es lento. La indexación de búsqueda inteligente de una importación grande puede ejecutarse durante horas en segundo plano. Es normal y no necesita una GPU.
- Un nombre de dominio que apunte al VPS. La aplicación móvil prefiere claramente un endpoint HTTPS, y conviene colocar un proxy inverso delante. La estructura es la misma que en una instancia de Nextcloud autohospedada con Docker, TLS y copias de seguridad; Immich es la alternativa para fotos de ese servidor de archivos.
- Docker y el complemento Compose instalados: Docker Engine y el complemento Compose v2 del repositorio apt oficial de Docker, exactamente como se explica en nuestra guía básica de Docker Compose.
Paso 1: Añada swap antes de cualquier otra cosa
El fallo más común de Immich en un VPS pequeño es que el kernel termine el contenedor ML por falta de memoria. Primero, proporcione al kernel espacio adicional para trabajar.
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -hfree -h ahora debería mostrar una línea Swap: de 4.0Gi. Esto no hará que ML sea rápido, pero evita que el contenedor muera a mitad de la indexación en una máquina con 4 GB.
Paso 2: Descargue los archivos oficiales de compose y de entorno; use esos archivos, no una copia
Immich fija las versiones de sus servicios y, especialmente, la imagen de la base de datos en los archivos que distribuye. No pegue un archivo de compose de un blog, incluido este, para usarlo como fuente de referencia. Descargue los recursos de la versión:
sudo mkdir -p /opt/immich && cd /opt/immich
sudo wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
sudo wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.envEstos archivos proceden de la versión etiquetada, por lo que las referencias de imagen coinciden. El archivo de compose define cuatro servicios. Conviene saber qué hace cada uno antes de modificar nada:
immich-server(ghcr.io/immich-app/immich-server, contenedorimmich_server), la API y la interfaz web, que escucha en el puerto2283. Monta las cargas que suba en/data.immich-machine-learning(ghcr.io/immich-app/immich-machine-learning, contenedorimmich_machine_learning), encargado de las búsquedas con CLIP y del reconocimiento facial. Almacena en caché los modelos descargados en un volumenmodel-cache. Es el servicio que más memoria consume.database(contenedorimmich_postgres), Postgres con la extensión vectorial VectorChord, que proporciona las búsquedas por similitud. La etiqueta de la imagen está fijada por digest directamente en el archivo de compose, por ejemploghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:.... Las instalaciones antiguas usabanpgvecto.rs; Immich eliminó su compatibilidad en la versión v3.0, por lo que cualquier instalación actual usa VectorChord. Nunca modifique esta etiqueta manualmente.redis(contenedorimmich_redis), una instancia de Valkey/Redis para las colas de trabajos.
Paso 3: Configure .env, donde se almacenan las fotos y la base de datos
Abra .env y configure cuatro valores. Todo lo que aparece debajo de la línea marcada debe permanecer sin cambios.
# Where original uploads are stored on the host
UPLOAD_LOCATION=/opt/immich/library
# Where the Postgres data lives. NEVER put this on an NFS/network share.
DB_DATA_LOCATION=/opt/immich/postgres
# "v3" is a floating tag that tracks the latest v3.x. Pin a full tag like
# v3.0.2 instead — then you upgrade on purpose, not by surprise.
IMMICH_VERSION=v3.0.2
# Change this to a long random string. Letters and digits only.
DB_PASSWORD=REPLACE_WITH_A_LONG_RANDOM_STRING
# Set your timezone so timestamps and "on this day" line up
TZ=Europe/London
###################################################################################
DB_USERNAME=postgres
DB_DATABASE_NAME=immichDos reglas que evitan problemas. UPLOAD_LOCATION debe apuntar al disco grande. Si más adelante conecta un volumen de datos, establezca desde el principio la ruta de montaje en este valor, porque cambiarla después implica mover las miniaturas y actualizar las rutas de los recursos. Además, DB_DATA_LOCATION debe estar en un disco local: Postgres se corrompe en un recurso compartido NFS o SMB, tal como indica claramente la documentación. Si usa sólo letras y dígitos en DB_PASSWORD, evitará una clase de errores de escape en las cadenas de conexión.
Paso 4: Primera ejecución y creación del usuario administrador
cd /opt/immich
sudo docker compose up -d
sudo docker compose psEl resultado correcto son cuatro contenedores, todos running y, finalmente, healthy:
NAME STATUS
immich_machine_learning Up (healthy)
immich_postgres Up (healthy)
immich_redis Up (healthy)
immich_server Up (healthy)La primera up descarga varios gigabytes de imágenes, así que espere. Supervise el progreso con sudo docker compose logs -f immich-server; cuando esté listo, el servidor registra que está escuchando en el puerto 2283. Abra http://YOUR_SERVER_IP:2283 en un navegador. En la primera visita aparece un asistente de Primeros pasos. La primera cuenta que cree será la de administrador. Establezca una contraseña segura. Esta cuenta controla la configuración del servidor, la gestión de usuarios y la configuración de ML que necesitará más adelante.
Paso 5: La aplicación móvil y la copia de seguridad en segundo plano
Instale "Immich" desde App Store o Play Store. En la pantalla de inicio de sesión, la aplicación solicita una Server Endpoint URL. Introduzca la URL completa, incluido el esquema; por ejemplo, https://photos.example.com (la aplicación añade /api automáticamente). Inicie sesión con la cuenta que acaba de crear. Después, abra la pantalla Backup de la aplicación, seleccione los álbumes que desea proteger (normalmente Camera y Screenshots) y active Background backup. iOS limita la frecuencia de las copias de seguridad en segundo plano. Las cargas en primer plano siempre se ejecutan. Las cargas en segundo plano se realizan cuando el sistema operativo lo permite.
Este es exactamente el punto en el que muchas personas se bloquean. Lea el Paso 6 antes de intentar solucionar el problema en la aplicación.
Paso 6: HTTPS mediante un proxy inverso y la regla de la URL completa
La aplicación móvil necesita HTTPS. Coloque un reverse proxy delante del puerto 2283 y termine TLS allí. Si ya ejecuta varios contenedores, Traefik con TLS automático para varias aplicaciones Docker es la opción más ordenada: un bloque de etiquetas encamina photos.example.com al contenedor immich-server y obtiene el certificado automáticamente. Si prefiere nginx, la guía Let's Encrypt con Certbot y nginx obtiene el certificado y un bloque proxy_pass http://127.0.0.1:2283;. Una vez creado el proxy, añadir el siguiente servicio consiste principalmente en crear otro subdominio. Así, un frontend multimedia como Halcyon, la interfaz de videoclub de los años 90 para Jellyfin puede ejecutarse junto a Immich en el mismo servidor. Lo mismo ocurre con HarnessRouter autoalojado, que coloca Codex y Claude Code detrás de una sola API. Este servicio se vincula deliberadamente a loopback y sólo queda accesible cuando el proxy termina TLS delante de él. Cambie su inicio de sesión predeterminado antes de apuntarle un subdominio. Sin embargo, no todos los contenedores necesitan un nombre de host público. Una herramienta sólo para administración, como un escáner de seguridad open-kritt autoalojado, es mejor dejarla completamente fuera del proxy y acceder a ella mediante un túnel SSH cuando necesite abrir su interfaz. Otros servicios omiten el proxy porque no usan HTTP, y un servidor relay RustDesk autoalojado es el ejemplo más claro: escucha en varios puertos TCP y UDP sin procesar y necesita reglas de firewall en lugar de un subdominio. Hay un ajuste del proxy importante para Immich: aumente el límite de tamaño de las cargas, porque los vídeos del teléfono ocupan mucho espacio. En nginx, ese ajuste es client_max_body_size 50000M; dentro del bloque server. El valor predeterminado de 1 MB rechaza las cargas de vídeo con 413 Request Entity Too Large.
La regla que aplica la aplicación es la siguiente: el endpoint debe ser accesible y, en la práctica, debe usar HTTPS. Los endpoints http:// o una IP directa sin especificar el puerto provocan el mensaje «la aplicación no puede acceder al servidor». Este fallo se describe con su propio nombre más abajo.
Paso 7: Bibliotecas externas frente a cargas, importar un árbol de fotos existente
Hay dos formas de introducir fotos en Immich y no son equivalentes.
- Las cargas son activos que pertenecen a Immich. La aplicación o el cargador web copia el archivo en
UPLOAD_LOCATION. Immich puede cambiarles el nombre, moverlos y eliminarlos. - Las bibliotecas externas son importaciones de solo lectura de archivos que ya se encuentran en una carpeta del servidor, en un árbol antiguo de
Pictureso en una exportación de un NAS. Immich los indexa en su ubicación y los muestra en la cronología, pero nunca modifica ni elimina los originales.
Para importar un árbol existente, móntelo en modo de solo lectura dentro del contenedor del servidor. Edite docker-compose.yml en immich-server: y añada un volumen:
immich-server:
volumes:
- ${UPLOAD_LOCATION}:/data
- /etc/localtime:/etc/localtime:ro
- /srv/photos:/mnt/media/photos:roLa opción :ro garantiza que Immich nunca pueda modificar los originales. Vuelva a crear el contenedor con sudo docker compose up -d. Después, en la interfaz web, vaya a su avatar → Administration → External Libraries → Create Library, seleccione el usuario propietario, haga clic en Add en Folders e introduzca la ruta del contenedor, /mnt/media/photos, no la ruta del host /srv/photos. Haga clic en Scan. Usar la ruta del host en lugar de la ruta del contenedor es el error más común con las bibliotecas externas: el análisis no encuentra nada e indica cero activos.
Paso 8: La disciplina de actualización que exige Immich
Esta es la parte que diferencia una instalación de Immich estable de una instalación averiada. Immich publica versiones rápidamente y no adapta correcciones a versiones anteriores ni admite downgrades. Seguir a ciegas la etiqueta flotante v3 acabará dañando la base de datos. Conviene aplicar el mismo hábito de fijar la versión y leer las notas a todos los contenedores de larga duración del servidor. Por eso un agente KiroCrew autoalojado se fija en una etiqueta conocida y funcional en lugar de poder cambiar sin control en el siguiente reinicio. La disciplina es la siguiente:
- Fije una versión. Mantenga
IMMICH_VERSIONestablecido en una etiqueta concreta comov3.0.2, no env3, que siempre obtiene la versión v3.x más reciente. - Lea las notas de la versión cada vez antes de actualizar. Allí se indican los cambios incompatibles, especialmente los relacionados con la base de datos o las extensiones vectoriales. La versión v3.0 es el ejemplo más claro: eliminó pgvecto.rs por completo, por lo que quienes todavía usaban la extensión antigua tuvieron que completar la migración a VectorChord (introducida en v1.133) antes de poder actualizar.
- Haga primero una copia de seguridad de la base de datos (paso 9). Siempre debe hacerlo, pero con mayor motivo cuando las notas mencionen la base de datos.
- Obtenga también el nuevo archivo de compose.
IMMICH_VERSIONsólo fija las imágenes del servidor y de ML. La imagen de Postgres se fija mediante un digest dentro dedocker-compose.yml, por lo que una versión que necesite una extensión de base de datos más reciente incluye un nuevo archivo de compose. Vuelva a descargar los dos recursos de la versión, aplique de nuevo los valores de.envy actualice. - Actualice los clientes móviles aproximadamente al mismo tiempo. El servidor sólo se comunica con su versión principal correspondiente, y la aplicación admite la versión principal actual y la anterior. Si el servidor se actualiza antes que la aplicación, el teléfono mostrará
Your app major version is not compatible with the server!hasta que la actualice. Por eso es más seguro actualizar primero la aplicación.
Los comandos concretos, una vez colocados los archivos nuevos, son los siguientes:
cd /opt/immich
sudo docker compose pull
sudo docker compose up -d
sudo docker image prunePaso 9: Copias de seguridad, un volcado de la base de datos MÁS los originales, y prueba de restauración
Una copia de seguridad de Immich consta de dos elementos, y uno sin el otro no sirve. La base de datos contiene la estructura de los álbumes, los rostros, los índices de búsqueda y la correspondencia entre cada recurso y su archivo. El directorio de originales contiene las fotografías reales. Si restaura uno sin el otro, obtendrá fotografías sin organización o una estructura vacía que apunta a archivos inexistentes. Esta estructura de dos partes no es una peculiaridad de Immich: un servicio de soporte Chatwoot autoalojado necesita la misma combinación de un volcado de Postgres y su directorio de cargas, o la bandeja de entrada restaurada volverá con todos los archivos adjuntos ausentes. Copiar el directorio de datos de Postgres como un árbol de archivos parece un atajo para evitar el volcado, pero no es una copia de seguridad utilizable; es una trampa que la guía completa de copia de seguridad y restauración de Immich explica junto con el error de restauración que deja una cronología vacía.
Vuelque la base de datos con pg_dump desde dentro del contenedor de Postgres, específicamente la base de datos immich, no todo el clúster:
sudo docker exec -t immich_postgres pg_dump --clean --if-exists \
--dbname=immich --username=postgres | gzip > /opt/immich/immich-db-$(date +%F).sql.gzDespués haga una copia de seguridad de UPLOAD_LOCATION, de todo el árbol /opt/immich/library y, en particular, de sus subdirectorios library/, upload/ y profile/, mediante restic, rsync o borg en otra máquina o en un almacenamiento de objetos. El mecanismo que programe esa tarea, ya sea una entrada de cron o un temporizador de systemd, necesita un lugar donde avisar cuando falle. Una unidad OnFailure= de systemd dirigida a su propio servidor push de ntfy envía un mensaje al teléfono la noche en que falla un volcado, en lugar de dejar que lo descubra durante una restauración. Haga primero la copia de la base de datos y después la de los archivos, para que el volcado nunca haga referencia a una fotografía que la copia de archivos todavía no haya guardado. Haga copias de seguridad independientes de las bibliotecas externas en su origen real; Immich no las administra.
Ahora viene la parte que todos omiten: pruebe la restauración. La restauración debe ejecutarse en una pila nueva cuyo servidor nunca se haya iniciado, sobre una imagen de Postgres cuya extensión vectorial sea compatible con el volcado. Por eso nunca debe improvisar la etiqueta de la imagen de la base de datos. En un equipo de pruebas con el mismo compose y .env, elimine cualquier estado anterior, inicie sólo la base de datos y cargue el volcado:
cd /opt/immich
sudo docker compose down -v
sudo docker compose pull
sudo docker compose create
sudo docker start immich_postgres
sleep 10
gunzip --stdout immich-db-2026-07-15.sql.gz |
sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" |
sudo docker exec -i immich_postgres psql --dbname=immich --username=postgres --single-transaction --set ON_ERROR_STOP=on
sudo docker compose up -dLa reescritura de search_path mediante sed no es opcional en una base de datos VectorChord. Si la omite, la restauración se interrumpe a mitad del proceso. Cuando la pila vuelva a iniciarse con los originales en su sitio, abra la interfaz web. Si aparecen sus fotografías y álbumes, la copia de seguridad funciona. Si nunca ha hecho esto, no tiene una copia de seguridad; sólo tiene una esperanza.
Modos de fallo y cadenas que verá
El contenedor de ML termina porque se queda sin memoria. sudo docker compose logs immich-machine-learning termina de forma abrupta, docker compose ps lo muestra como Restarting y el código de salida es 137. sudo dmesg | grep -i oom lo confirma: Out of memory: Killed process ... (python3). Los trabajos de búsqueda y reconocimiento facial se quedan bloqueados. La causa es que hay muy poca RAM para los modelos. Soluciones, en este orden: añada swap (Paso 1); asigne más RAM a la VPS; o, si realmente no puede hacerlo, desactive ML en Administration → Settings → Machine Learning Settings desactivando Smart Search y Facial Recognition. Conservará las copias de seguridad y los álbumes, pero perderá la búsqueda por contenido. Eliminar el servicio immich-machine-learning del archivo compose produce el mismo efecto.
Postgres no se inicia después de una actualización. El registro del servidor repite una línea como The database currently has VectorChord 0.5.3 activated, but the Postgres instance only has 0.4.2 available. This most likely means the extension was downgraded. o, en stacks antiguos, The pgvecto.rs extension is not available in this Postgres instance.. La causa es una imagen de base de datos cuya versión de la extensión es anterior a la versión a la que se actualizaron los datos. Esto ocurre casi siempre por editar manualmente la etiqueta de la imagen o por restaurar un dump más reciente en una imagen antigua. La solución es usar la imagen de Postgres correspondiente, obtener el archivo compose de la release que corresponda a la base de datos, no hacer downgrade y restaurar sólo en una imagen compatible.
La aplicación móvil no puede conectarse al servidor. La pantalla de inicio de sesión muestra un error de conexión o Server is not reachable después de introducir la URL. Hay tres causas: escribió http://, pero el proxy sólo sirve https://; se conectó directamente al backend, pero omitió el puerto, por lo que intentó example.com (puerto 443) en lugar de example.com:2283; o el reverse proxy no reenvía /api. Corríjalo introduciendo la URL completa https://photos.example.com y confirme primero que se carga en el navegador del teléfono. Si funciona en el navegador pero no en la aplicación, el proxy está eliminando la ruta o el certificado es autofirmado; la aplicación rechaza los certificados que no son de confianza.
Se agota el espacio en disco durante la importación. Las cargas empiezan a fallar, las miniaturas aparecen vacías y los registros muestran ENOSPC: no space left on device o, desde Postgres, could not extend file ... No space left on device. df -h muestra que el volumen UPLOAD_LOCATION está al 100%. Por eso debe dimensionar el disco antes de importar una biblioteca grande. Para recuperarse, conecte un volumen más grande, detenga el stack, mueva UPLOAD_LOCATION a ese volumen, actualice .env y vuelva a iniciar el stack. También puede ampliar el disco existente si su proveedor lo permite. Postgres puede quedar bloqueado si se llena el disco. Libere espacio y reinicie el contenedor de la base de datos antes de asumir que hay corrupción.
FAQ
¿Cuánta RAM y espacio de disco necesita Immich?
Los requisitos oficiales de Immich son 6 GB de RAM como mínimo y 8 GB como cantidad recomendada. Para una biblioteca pequeña, 4 GB con swap es el mínimo práctico. Configure swap en cualquier caso, porque el contenedor de aprendizaje automático es el que genera los picos de consumo. Para el disco, reserve el tamaño completo de la biblioteca más aproximadamente un 10–20 % para las miniaturas y vistas previas generadas. Use almacenamiento local y no coloque nunca el directorio de datos de Postgres en un recurso compartido de red. Si todavía está decidiendo qué más ejecutar, la guía sobre qué alojar uno mismo en 2026 compara el consumo de Immich con el de otros servicios.
¿Puedo ejecutar Immich sin una GPU?
Sí. El contenedor de aprendizaje automático funciona correctamente con la CPU. Una GPU sólo acelera la indexación de búsquedas inteligentes y, con la variante de imagen adecuada, la transcodificación de vídeo. En la CPU, la indexación inicial de una biblioteca grande puede tardar horas en segundo plano, pero no bloquea las copias de seguridad ni la navegación. Si el equipo es demasiado pequeño para ejecutar el aprendizaje automático, puede desactivar Smart Search y Facial Recognition en la configuración de administración y mantener todo lo demás.
¿Cómo actualizo Immich de forma segura?
Fije IMMICH_VERSION a una etiqueta concreta, como v3.0.2, lea las notas de la versión antes de cada actualización y haga primero una copia de seguridad de la base de datos. Como la imagen de Postgres está fijada dentro de docker-compose.yml y no mediante IMMICH_VERSION, vuelva a descargar tanto el archivo compose como example.env de la versión de destino, vuelva a aplicar sus valores y ejecute docker compose pull && docker compose up -d. No deje nunca que la versión se actualice sin supervisión. Immich publica cambios incompatibles y no admite volver a versiones anteriores.
¿Qué debo incluir exactamente en la copia de seguridad?
Dos elementos, juntos: un pg_dump de la base de datos immich y el directorio completo de originales UPLOAD_LOCATION. La base de datos contiene los álbumes, las caras y la correspondencia entre activos y archivos. El directorio contiene las fotos reales. Para restaurar el sistema necesita ambos elementos y una imagen de la base de datos con una extensión vectorial compatible. Haga primero el volcado de la base de datos y copie después los archivos. Pruebe la restauración al menos una vez en un equipo de pruebas; una copia de seguridad no probada no es una copia de seguridad.
¿Cómo importo mi carpeta de fotos existente?
Monte la carpeta en modo de sólo lectura en el contenedor immich-server como un volumen adicional, por ejemplo - /srv/photos:/mnt/media/photos:ro, vuelva a crear el contenedor y, en Administration → External Libraries, cree una biblioteca y añada la ruta del contenedor /mnt/media/photos. Immich indexa los archivos en su ubicación y nunca los modifica ni los elimina. El error más habitual es introducir la ruta del host en lugar de la ruta del contenedor, lo que hace que el escaneo no encuentre ningún archivo.