Deploy Immich in one click
Your photos and videos, backed up from your phone, with smart search, faces and albums
A self-hosted alternative to Google Photos and iCloud Photos.
What is Immich?
Immich is a self-hosted photo and video library that people run in place of Google Photos or iCloud Photos. Its apps for iPhone and Android back up the camera roll in the background, and the web page and the apps show everything on one timeline, with albums, favourites, an archive, a map of where each photo was taken and memories of the same day in earlier years.
Machine learning that runs within Immich, with no cloud service, finds the people in your photos and groups their faces, reads text in images, and powers smart search, which finds a photo from words such as “dog on a beach”. Immich keeps RAW files, Live Photos and motion photos, shares albums or links with anyone, and lets a partner see your library. Each person on the server has an account of their own.
This template runs Immich’s server, its machine learning, Valkey and its PostgreSQL with the vector extensions it needs, its library in a volume. The administrator is made from the email and password you give before Immich answers anyone, and shared links get the project’s address. Immich asks for 6 GB of memory and 2 cores at least, so it fits Grow; your photos take the disk, so watch its size as the library grows.
Hosting Immich on Pethost
- You sign in to Pethost with a passkey, GitHub or Google. No machine yet? You pick a plan, and the link waits until it is ready.
- New project opens at Immich. You fill in what it asks for and click Create project: Email, Admin password.
- In a minute or two Immich is live at its own address with HTTPS. Its secrets are made at random, and its data is in the nightly backup.
- It is a project like any other: you or your agent can change it, roll it back or restore it.
Questions about Immich
Is Immich free?
Yes. Immich is open source under the AGPL-3.0 license, and its makers say all its features are free forever, with nothing behind a paywall. You can buy it for yourself or for a server to support the project, which unlocks nothing more.
How do I back up my phone’s photos to Immich?
Install the Immich app from the App Store or Google Play, give your Immich’s address as its Server Endpoint URL, sign in, and turn on Backup for the albums you choose. It uploads new photos and videos in the background, and skips what is already there.
Does Immich recognise faces?
Yes. Its machine learning, which runs within Immich and sends your photos nowhere, detects faces in each photo and groups them into People, which you name. It also reads text in images, and its smart search finds photos by what is in them.
How much memory and disk does Immich need?
Immich asks for 6 GB of memory and 2 cores at least, and here takes about 1.5 GB at rest, about 2.5 GB while machine learning works through new photos, and more at the first upload, while it downloads its models. The disk holds your originals plus thumbnails and transcoded videos, so plan for more than the library’s own size.
Is Immich safe for my only copy of my photos?
Immich itself says to keep a 3-2-1 backup of photos you care about: three copies, on two kinds of storage, one of them elsewhere. Pethost backs up the project’s volumes every night, and Immich also dumps its database by itself; keep the originals somewhere else too.
How much memory and disk does Immich need?
Immich takes about 1.5 GB of memory at rest with its database and machine learning, about 2.5 GB while it works through new photos, and more at the first upload, while it downloads its models; the disk is your library plus its thumbnails and videos. On Pethost the Grow plan is enough for it, with room for other apps beside it.
Immich Docker Compose file
The template is this compose.yaml and the variables written into the project’s .env as it is created. Pethost runs the file as it is written.
Containers: immich-server (ghcr.io/immich-app/immich-server:v3), immich-machine-learning (ghcr.io/immich-app/immich-machine-learning:v3), redis (valkey/valkey:9), database (ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0).
Volumes, in the nightly backup: library, db, model-cache.
services:
immich-server:
image: ghcr.io/immich-app/immich-server:v3
restart: unless-stopped
environment:
DB_HOSTNAME: database
DB_USERNAME: postgres
DB_PASSWORD: ${DB_PASSWORD}
DB_DATABASE_NAME: immich
REDIS_HOSTNAME: redis
TZ: ${TZ:-Etc/UTC}
URL: ${URL}
ADMIN_EMAIL: ${ADMIN_EMAIL}
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
# A new Immich makes whoever opens it first its administrator. So its first start runs it on
# port 2284, which nobody reaches, signs the administrator up there with the email and password
# given, and sets the address shared links use; only then does Immich answer on 2283.
command:
- |
set -e
if [ ! -f /data/.pethost-admin ]; then
# In a process group of its own, so that its workers stop with it.
IMMICH_PORT=2284 setsid start.sh &
setup=$$!
SETUP_PID=$$setup node -e '
const base = "http://127.0.0.1:2284/api";
const { URL: url, ADMIN_EMAIL: email, ADMIN_PASSWORD: password } = process.env;
const json = { "content-type": "application/json" };
const call = async (path, options) => {
const response = await fetch(base + path, options);
if (!response.ok) throw new Error(path + ": " + response.status + " " + (await response.text()));
return response.json();
};
const setUp = async () => {
const { isInitialized } = await call("/server/config");
if (isInitialized) return;
const name = email.split("@")[0];
await call("/auth/admin-sign-up", { method: "POST", headers: json, body: JSON.stringify({ email, password, name }) });
const { accessToken } = await call("/auth/login", { method: "POST", headers: json, body: JSON.stringify({ email, password }) });
const headers = { ...json, authorization: "Bearer " + accessToken };
const config = await call("/system-config", { headers });
config.server.externalDomain = url;
await call("/system-config", { method: "PUT", headers, body: JSON.stringify(config) });
};
(async () => {
let failure;
for (let i = 0; i < 300; i++) {
// Immich stops if it cannot start: the container starts again and so does this.
try {
process.kill(Number(process.env.SETUP_PID), 0);
} catch {
process.exit(1);
}
try {
return await setUp();
} catch (error) {
failure = error;
await new Promise((done) => setTimeout(done, 1000));
}
}
console.error(failure);
process.exit(1);
})();
'
kill -- -$$setup
wait $$setup || true
while kill -0 -- -$$setup 2>/dev/null; do sleep 1; done
touch /data/.pethost-admin
fi
unset ADMIN_PASSWORD
exec start.sh
volumes:
- library:/data
depends_on:
redis:
condition: service_started
database:
condition: service_healthy
# Its first start makes the database and the administrator before it answers on 2283.
healthcheck:
test: ["CMD-SHELL", "immich-healthcheck"]
interval: 30s
start_period: 10m
start_interval: 5s
immich-machine-learning:
image: ghcr.io/immich-app/immich-machine-learning:v3
restart: unless-stopped
volumes:
- model-cache:/cache
redis:
image: valkey/valkey:9
restart: unless-stopped
healthcheck:
test: redis-cli ping | grep -q PONG || exit 1
database:
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
restart: unless-stopped
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: immich
POSTGRES_INITDB_ARGS: --data-checksums
shm_size: 128mb
volumes:
- db:/var/lib/postgresql/data
volumes:
library:
db:
model-cache:
More in Media
- OwncastLive video and chat on a page of your own
- NavidromeA music server that streams your own collection
- RomMA library and in-browser player for your retro games
- KomgaA server and web reader for your comics, manga and ebooks
- LycheeA photo gallery to upload, sort and share your photos
- AudiobookshelfYour own audiobook and podcast server: listen in the browser or the apps, with progress kept across devices