SSD Nodes Learn Hosting plans →
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-28

Immich self-hosting: servono davvero 6 GB di RAM?

Scopri quanta RAM serve davvero, come usare la porta 2283 dietro HTTPS, risolvere l'errore exit 137 e ripristinare il database prima dell'upgrade a Immich v3.

Cosa stai realizzando

Immich è un servizio self-hosted per il backup di foto e video, una vera alternativa a Google Photos. Include un'app per smartphone che carica in background il rullino fotografico, una timeline, album, riconoscimento dei volti e una ricerca basata sul machine learning che trova "spiaggia" o una persona senza dover aggiungere tag. Lo esegui su un VPS di tua proprietà, i file originali restano sul tuo disco e nessuno li analizza per venderti qualcosa. Se stai ancora valutando l'altra alternativa più ovvia, il nostro confronto tra PhotoPrism e Immich mette a confronto requisiti minimi di RAM, app per smartphone e comandi di backup.

L'installazione consiste in quattro container definiti nel file Docker Compose del progetto. Questa parte richiede dieci minuti. Il resto di questa guida riguarda i problemi più complessi: il container di machine learning richiede molta memoria su una macchina di dimensioni ridotte, gli originali consumano rapidamente spazio su disco, l'app mobile rifiuta un server che usa semplice HTTP e Immich introduce modifiche incompatibili abbastanza spesso da consentire a un docker compose pull eseguito senza attenzione di impedire l'avvio del database. Prendi sul serio questi quattro aspetti e Immich sarà estremamente affidabile. Ignorali e perderai un intero fine settimana.

Prerequisiti e aspetti da considerare

  • RAM: la documentazione ufficiale indica 6 GB come minimo e 8 GB come valore consigliato; considera 4 GB più swap come soglia minima assoluta. I container immich-server e Postgres richiedono poche risorse. Il container immich-machine-learning è quello che consuma più RAM: carica i modelli CLIP e di riconoscimento facciale in memoria per creare gli indici di ricerca e, su un server con 2 GB, il kernel lo termina. Aggiungi lo swap anche se disponi di 4 GB.
  • Disco: dimensiona lo spazio per l'intera libreria, lasciando anche un margine. Gli originali vengono copiati integralmente. Immich genera inoltre miniature e immagini di anteprima, con un incremento indicativo del 10–20%. Una raccolta fotografica di 200 GB richiede un volume da 300 GB. Rispetto a questi dati, Postgres occupa poco spazio.
  • CPU: qualsiasi VPS KVM moderno è adeguato, ma l'elaborazione ML sulla CPU è lenta. L'indicizzazione per la ricerca intelligente di una grande importazione può richiedere diverse ore ed eseguirsi in background. È normale e non richiede una GPU.
  • Un nome di dominio che punti al VPS. L'app mobile preferisce nettamente un endpoint HTTPS e conviene configurare un reverse proxy davanti al servizio. La struttura è la stessa di una istanza Nextcloud self-hosted con Docker, TLS e backup: Immich è l'equivalente per le foto di quel server per i file.
  • Docker e il plugin Compose installati: Docker Engine insieme al plugin Compose v2 dal repository apt ufficiale di Docker, esattamente come descritto nella nostra guida introduttiva a Docker Compose.

Passaggio 1: aggiungi lo swap prima di tutto

Il problema più comune di Immich su un VPS di piccole dimensioni è la terminazione del container ML da parte del sistema per esaurimento della memoria. Prima assegna al kernel uno spazio di swap.

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 -h

free -h dovrebbe ora mostrare una riga Swap: di 4.0Gi. Questa configurazione non rende ML più veloce, ma impedisce al container di terminare durante l'indicizzazione su una macchina con 4 GB.

Passaggio 2: Scaricare i file compose ed env ufficiali e usare quelli, non una copia

Immich fissa le versioni dei servizi e, soprattutto, l'immagine del database nei file che distribuisce. Non utilizzare come riferimento un file compose copiato da un blog, incluso questo. Scarica gli asset della release:

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.env

Questi file provengono dalla release contrassegnata, quindi i riferimenti alle immagini sono allineati. Il file compose definisce quattro servizi. Prima di modificarlo, è utile sapere a cosa serve ciascun servizio:

  • immich-server (ghcr.io/immich-app/immich-server, container immich_server), l'API e l'interfaccia web, in ascolto sulla porta 2283. Monta i caricamenti in /data.
  • immich-machine-learning (ghcr.io/immich-app/immich-machine-learning, container immich_machine_learning), per la ricerca CLIP e il riconoscimento dei volti. Memorizza nella cache i modelli scaricati in un volume model-cache. È il servizio che richiede più memoria.
  • database (container immich_postgres), PostgreSQL con l'estensione vettoriale VectorChord, che gestisce la ricerca per similarità. Il tag dell'immagine è fissato tramite digest direttamente nel file compose, ad esempio ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:.... Le configurazioni precedenti usavano pgvecto.rs; il supporto è stato rimosso in Immich v3.0, quindi tutte le installazioni attuali usano VectorChord. Non modificare manualmente questo tag.
  • redis (container immich_redis), un'istanza Valkey/Redis per le code dei job.

Passaggio 3: configura .env, dove vengono archiviati le foto e il database

Apri .env e imposta quattro valori. Tutto ciò che segue la riga contrassegnata deve rimanere invariato.

# 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=immich

Due impostazioni evitano problemi. UPLOAD_LOCATION deve puntare al disco più capiente. Se in seguito colleghi un volume dati, impostalo fin dall’inizio sul relativo percorso di mount, perché spostarlo successivamente richiede di trasferire le miniature e aggiornare i percorsi degli asset. Anche DB_DATA_LOCATION deve trovarsi su un disco locale: PostgreSQL su una condivisione NFS o SMB può danneggiare i dati, come indicato chiaramente nella documentazione. Se in DB_PASSWORD usi soltanto lettere e cifre, eviti una categoria di problemi dovuti all'escaping nella stringa di connessione.

Passaggio 4: primo avvio e creazione dell’utente amministratore

cd /opt/immich
sudo docker compose up -d
sudo docker compose ps

Il risultato corretto è composto da quattro container, tutti running e infine healthy:

NAME                      STATUS
immich_machine_learning   Up (healthy)
immich_postgres           Up (healthy)
immich_redis              Up (healthy)
immich_server             Up (healthy)

Il primo up scarica diversi gigabyte di immagini, quindi attendi il completamento. Monitora l’avanzamento con sudo docker compose logs -f immich-server; quando è pronto, il server registra nei log che è in ascolto sulla porta 2283. A questo punto apri http://YOUR_SERVER_IP:2283 in un browser. Alla prima visita viene visualizzata una procedura guidata Getting Started; il primo account creato è l’amministratore. Imposta una password complessa: questo account gestisce le impostazioni del server, gli utenti e la configurazione ML che ti servirà in seguito.

Passaggio 5: l’app mobile e il backup in background

Installa "Immich" dall’App Store o dal Play Store. Nella schermata di accesso viene richiesto un Server Endpoint URL. Inserisci l’URL completo, incluso lo schema, ad esempio https://photos.example.com (l’app aggiunge automaticamente /api). Accedi con l’account appena creato, quindi apri la schermata Backup dell’app, seleziona gli album da proteggere (di solito Camera e Screenshots) e abilita Background backup. In iOS il backup in background è limitato dal sistema operativo; i caricamenti in primo piano vengono sempre eseguiti, mentre quelli in background vengono eseguiti quando il sistema operativo lo consente.

È esattamente in questo punto che molte persone incontrano difficoltà, quindi leggi il Passaggio 6 prima di cercare di risolvere il problema nell’app.

Passaggio 6: HTTPS tramite un reverse proxy e regola dell’URL completo

L’app mobile richiede HTTPS. Inserisci un reverse proxy davanti alla porta 2283 e termina TLS sul proxy. Se esegui già diversi container, Traefik con TLS automatico per più applicazioni Docker è l’opzione più ordinata: un unico blocco di label instrada photos.example.com verso il container immich-server e recupera il certificato automaticamente. Se preferisci nginx, la guida Let's Encrypt con Certbot e nginx ottiene il certificato e un blocco proxy_pass http://127.0.0.1:2283;. Una volta disponibile il proxy, aggiungere il servizio successivo richiede soprattutto un nuovo sottodominio. È così che un front-end multimediale come Halcyon, il tema da videonoleggio degli anni 90 per Jellyfin può affiancare Immich sullo stesso server. Lo stesso vale per HarnessRouter self-hosted che espone Codex e Claude Code tramite un’unica API, che si associa intenzionalmente all’interfaccia loopback e diventa raggiungibile solo quando il proxy termina TLS davanti al servizio. Cambia quindi il login predefinito prima di associargli un sottodominio. Non tutti i container devono avere un hostname pubblico. Uno strumento riservato agli amministratori, come un security scanner open-kritt self-hosted, è preferibile lasciarlo completamente fuori dal proxy e raggiungerne l’interfaccia tramite un tunnel SSH solo quando serve aprirla. Altri servizi non usano il proxy perché HTTP non è il loro protocollo. Un server relay RustDesk self-hosted è l’esempio più chiaro: resta in ascolto su alcune porte TCP e UDP non incapsulate e richiede regole del firewall, non un sottodominio. Per Immich è importante una configurazione del proxy: aumenta il limite delle dimensioni degli upload, perché i video dei telefoni possono essere grandi. In nginx, inserisci client_max_body_size 50000M; nel blocco server. Il valore predefinito di 1 MB rifiuta gli upload video con 413 Request Entity Too Large.

La regola applicata dall’app è la seguente: l’endpoint deve essere raggiungibile e, in pratica, deve usare HTTPS. Gli endpoint http:// o un indirizzo IP diretto senza porta specificata causano l’errore "l’app non riesce a raggiungere il server", descritto più avanti come errore specifico.

Passaggio 7: librerie esterne e caricamenti, importare un albero di foto esistente

Esistono due modi per inserire le foto in Immich e non sono equivalenti.

  • I caricamenti sono risorse gestite da Immich. L'applicazione o il caricatore web copia il file in UPLOAD_LOCATION. Immich può rinominarli, spostarli ed eliminarli.
  • Le librerie esterne sono importazioni in sola lettura di file già presenti in una directory del server, ad esempio un vecchio albero Pictures o un'esportazione da un NAS. Immich li indicizza nella posizione originale e li mostra nella timeline, ma non modifica né elimina mai i file originali.

Per importare un albero esistente, montalo in sola lettura nel container del server. Modifica docker-compose.yml in immich-server: e aggiungi un volume:

  immich-server:
    volumes:
      - ${UPLOAD_LOCATION}:/data
      - /etc/localtime:/etc/localtime:ro
      - /srv/photos:/mnt/media/photos:ro

Il parametro :ro garantisce che Immich non possa mai modificare i file originali. Ricrea il container con sudo docker compose up -d, quindi nell'interfaccia web vai al tuo avatar → Amministrazione → Librerie esterne → Crea libreria, seleziona l'utente proprietario, fai clic su Aggiungi nella sezione Cartelle e inserisci il percorso del container, /mnt/media/photos, non il percorso dell'host /srv/photos. Fai clic su Scansiona. Usare il percorso dell'host invece di quello del container è l'errore più comune nelle librerie esterne: la scansione non trova nulla e segnala zero risorse.

La disciplina degli aggiornamenti richiesta da Immich

Questa è la fase che distingue un’installazione Immich funzionante da una compromessa. Immich rilascia rapidamente nuove versioni e non esegue il backport delle correzioni né supporta il downgrade. Seguire ciecamente il tag mobile v3 finirà per compromettere il database. La stessa abitudine di fissare una versione e leggere le note di rilascio va applicata a ogni container a lunga durata sul server. Per questo un agente KiroCrew self-hosted va fissato a un tag noto e verificato, invece di consentirgli di cambiare versione al riavvio successivo. La procedura è questa:

  1. Fissa una versione. Mantieni IMMICH_VERSION impostato su un tag concreto come v3.0.2, non sul tag mobile v3, che recupera sempre la versione più recente di v3.x.
  2. Leggi sempre le note di rilascio prima di eseguire l’aggiornamento. Le modifiche incompatibili, soprattutto quelle relative al database o alle estensioni vettoriali, sono indicate nelle note. La release v3.0 è l’esempio più evidente: ha rimosso completamente pgvecto.rs. Chi utilizzava ancora la vecchia estensione doveva quindi completare la migrazione a VectorChord, introdotta già in v1.133, prima di poter aggiornare.
  3. Esegui prima il backup del database (Passo 9). Sempre, ma a maggior ragione quando le note menzionano il database.
  4. Scarica anche il nuovo file compose. IMMICH_VERSION fissa soltanto le immagini del server e di ML. L’immagine Postgres è fissata tramite digest all’interno di docker-compose.yml. Di conseguenza, una versione che richiede un’estensione del database più recente include un nuovo file compose. Scarica nuovamente entrambi gli asset della release, riapplica i valori .env, quindi esegui l’aggiornamento.
  5. Aggiorna i client mobili nello stesso periodo. Il server comunica soltanto con la versione major corrispondente e l’app supporta la versione major corrente e quella precedente. Se il server viene aggiornato prima dell’app, sul telefono viene visualizzato Your app major version is not compatible with the server! finché non aggiorni l’app. Per questo è più sicuro aggiornare prima l’app.

I comandi effettivi, dopo aver sostituito i file con le nuove versioni, sono:

cd /opt/immich
sudo docker compose pull
sudo docker compose up -d
sudo docker image prune

Passo 9: backup, dump del database e file originali, quindi test del ripristino

Il backup di Immich comprende due elementi, e uno senza l'altro è inutile. Il database contiene la struttura degli album, i volti, gli indici di ricerca e la corrispondenza tra asset e file. La directory degli originali contiene le foto effettive. Se ripristini uno solo dei due elementi, ottieni foto prive di organizzazione oppure una struttura vuota che punta a file mancanti. Questa struttura in due parti non è una peculiarità di Immich: un help desk Chatwoot self-hosted richiede lo stesso abbinamento tra un dump Postgres e la relativa directory degli upload, altrimenti la casella ripristinata torna con tutti gli allegati mancanti. Copiare la directory dei dati di Postgres come struttura di file sembra una scorciatoia per evitare il dump, ma non costituisce un backup utilizzabile: è una trappola che la guida completa al backup e al ripristino di Immich analizza insieme al ripristino incompleto che lascia la timeline vuota.

Esegui il dump del database con pg_dump dall'interno del container Postgres, nello specifico del database immich, non dell'intero cluster:

sudo docker exec -t immich_postgres pg_dump --clean --if-exists \
  --dbname=immich --username=postgres | gzip > /opt/immich/immich-db-$(date +%F).sql.gz

Esegui quindi il backup di UPLOAD_LOCATION, dell'intera struttura /opt/immich/library e in particolare delle relative sottocartelle library/, upload/ e profile/, usando restic, rsync o borg verso un'altra macchina o un object storage. Qualunque sistema pianifichi questa attività, un'entry cron o un timer systemd, deve avere un punto in cui segnalare gli errori. Un'unità systemd OnFailure= configurata per puntare a il tuo server push ntfy invia una notifica al telefono la notte in cui un dump non riesce, invece di lasciarti scoprire il problema durante un ripristino. Esegui prima il backup del database e poi quello dei file, così il dump non fa mai riferimento a una foto che il backup dei file non ha ancora copiato. Esegui il backup delle librerie esterne separatamente, dalla loro sorgente effettiva: Immich non le gestisce.

Ora arriva la parte che tutti saltano: testa il ripristino. Il ripristino deve essere eseguito su uno stack nuovo, il cui server non sia mai stato avviato, usando un'immagine Postgres con un'estensione vector compatibile con il dump. Per questo non devi improvvisare il tag dell'immagine DB. Su una macchina di prova con lo stesso compose e .env, elimina ogni stato precedente, avvia solo il database e carica il dump:

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 -d

La riscrittura di search_path tramite sed non è facoltativa su un database VectorChord: se la ometti, il ripristino si interrompe a metà. Quando lo stack torna operativo con gli originali al loro posto, apri l'interfaccia web: se le foto e gli album sono presenti, il backup funziona. Se non hai mai eseguito questa procedura, non hai un backup: hai solo una speranza.

Modalità di errore, con le stringhe visualizzate

Il container ML viene terminato per esaurimento della memoria. sudo docker compose logs immich-machine-learning termina improvvisamente, docker compose ps mostra Restarting e il codice di uscita è 137. sudo dmesg | grep -i oom lo conferma: Out of memory: Killed process ... (python3). I job di ricerca e riconoscimento facciale rimangono quindi bloccati. La causa è una quantità insufficiente di RAM per i modelli. Gli interventi possibili, in ordine, sono: aggiungere swap (Passaggio 1); aumentare la RAM della VPS; oppure, se non è realmente possibile, disabilitare ML in Amministrazione → Impostazioni → Impostazioni del machine learning disattivando Ricerca intelligente e Riconoscimento facciale. In questo modo si conservano backup e album, ma si perde la ricerca per contenuto. Rimuovere il servizio immich-machine-learning dal file compose produce lo stesso effetto.

Postgres non si avvia dopo un aggiornamento. Il log del server si ripete con una riga simile a 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. oppure, negli stack meno recenti, a The pgvecto.rs extension is not available in this Postgres instance.. La causa è un'immagine del database con una versione dell'estensione più vecchia di quella a cui sono stati aggiornati i dati. Questo accade quasi sempre dopo avere modificato manualmente il tag dell'immagine o avere ripristinato un dump più recente su un'immagine meno recente. La soluzione consiste nell'utilizzare l'immagine Postgres corrispondente, recuperare il file compose dalla release compatibile con il database, non eseguire il downgrade e ripristinare i dati soltanto su un'immagine compatibile.

L'app mobile non riesce a raggiungere il server. Dopo l'inserimento dell'URL, la schermata di accesso mostra un errore di connessione o Server non raggiungibile. Le cause principali sono tre: è stato inserito http:// mentre il proxy serve soltanto https://; ci si è connessi direttamente al backend senza specificare la porta, quindi l'app ha tentato example.com (porta 443) invece di example.com:2283; oppure il reverse proxy non inoltra /api. Inserire l'URL completo https://photos.example.com e verificare prima che venga caricato nel browser del telefono. Se il browser funziona ma l'app no, il proxy potrebbe rimuovere il percorso oppure il certificato potrebbe essere autofirmato; l'app rifiuta i certificati non attendibili.

Lo spazio su disco si esaurisce durante l'importazione. I caricamenti iniziano a fallire, le anteprime diventano vuote e i log mostrano ENOSPC: no space left on device oppure, da Postgres, could not extend file ... No space left on device. df -h mostra che il volume UPLOAD_LOCATION è al 100%. Per questo è necessario dimensionare lo spazio su disco prima di importare una libreria di grandi dimensioni. Per il ripristino, collegare un volume più capiente, arrestare lo stack, spostare UPLOAD_LOCATION sul nuovo volume, aggiornare .env e riavviare lo stack; in alternativa, espandere il disco esistente se il provider lo consente. Se lo spazio si esaurisce, Postgres può bloccarsi. Liberare spazio e riavviare il container del database prima di ipotizzare una corruzione.

FAQ

Quanta RAM e spazio su disco richiede Immich?

I requisiti ufficiali di Immich indicano 6 GB di RAM come minimo e 8 GB come valore consigliato. Per una libreria di piccole dimensioni, 4 GB con swap rappresentano il limite pratico minimo. Configura comunque la swap, perché è il container di machine learning a generare i picchi di utilizzo. Per lo spazio su disco, considera l’intera dimensione della libreria e aggiungi circa il 10–20% per miniature e anteprime generate. Usa lo storage locale e non collocare mai la directory dei dati di Postgres su una condivisione di rete. Se stai ancora decidendo quali altri servizi eseguire, la guida a cosa eseguire in self-hosting nel 2026 confronta l’impronta di Immich con quella di altri servizi.

Posso eseguire Immich senza una GPU?

Sì. Il container di machine learning funziona correttamente sulla CPU. Una GPU accelera soltanto l’indicizzazione per la ricerca intelligente e, con la variante di immagine appropriata, la transcodifica video. Sulla CPU, l’indicizzazione iniziale di una libreria di grandi dimensioni può richiedere diverse ore in background, ma non blocca i backup né la consultazione della libreria. Se il server è troppo limitato per eseguire il machine learning, puoi disabilitare Smart Search e Facial Recognition nelle impostazioni di amministrazione e mantenere attive tutte le altre funzioni.

Come posso aggiornare Immich in sicurezza?

Blocca IMMICH_VERSION su un tag concreto, ad esempio v3.0.2, leggi le note di rilascio prima di ogni aggiornamento ed esegui prima il backup del database. Poiché l’immagine Postgres è fissata all’interno di docker-compose.yml e non tramite IMMICH_VERSION, scarica di nuovo sia il file compose sia example.env dalla release di destinazione, riapplica i tuoi valori e poi esegui docker compose pull && docker compose up -d. Non lasciare mai la versione non vincolata senza supervisione: Immich introduce modifiche incompatibili e non supporta il downgrade.

Che cosa devo includere esattamente nel backup?

Due elementi, insieme: un pg_dump del database immich e l’intera directory degli originali UPLOAD_LOCATION. Il database contiene album, volti e la corrispondenza tra asset e file. La directory contiene le fotografie effettive. Per un ripristino servono entrambi gli elementi, oltre a un’immagine del database con un’estensione vettoriale compatibile. Esegui prima il dump del database e poi la copia dei file. Verifica il ripristino almeno una volta su un server di test: un backup non verificato non è un backup.

Come posso importare la cartella di fotografie esistente?

Monta la cartella in sola lettura nel container immich-server come volume aggiuntivo, ad esempio - /srv/photos:/mnt/media/photos:ro, ricrea il container, quindi in Administration → External Libraries crea una libreria e aggiungi il percorso del container /mnt/media/photos. Immich indicizza i file nella posizione originale e non li modifica né li elimina. L’errore più comune consiste nell’inserire il percorso dell’host invece di quello del container. In questo caso la scansione non trova alcun file.