Immich
Immich: self-hosted photo and video backup, with machine learning on CPU
Immich on Cubeship
Immich is a self-hosted photo and video backup: phone apps that upload in the background, a web timeline, albums and sharing, and search by faces, places and what is in the picture. This template installs it on a Cubeship instance.
What it creates
- immich — the Immich server
v3.2.0: the web app, the API the phone apps talk to, and the background jobs that make thumbnails and transcode videos. It answers on the domain you choose and keeps every upload in a volume at/data. - machine-learning — Immich's machine learning
v3.2.0, on the CPU: smart search, face detection and recognition, and text in images. It has no domain, listens on port3003inside the instance, and keeps the models it downloads in a volume at/cache. - postgres — Immich's own Postgres 14 with VectorChord, built from
database/Dockerfile. It has no domain, listens on port5432inside the instance, and keeps its data in a volume at/var/lib/postgresql/data. - immich-redis — a managed Redis 7.4, where Immich queues its jobs.
They are deployed in that order: the database, then machine learning, then the server.
It needs Cubeship 0.7.0 or newer, and an admin to install it: the database is built on the instance, and only admins build.
Why the database is its own app, and built
Immich stores its search and face embeddings with the VectorChord extension.
The managed Postgres is the plain postgres image, which does not have it, so
the template runs the image Immich's own
compose file
runs, ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0, as an
app with a volume. As its own container, Postgres makes Immich's user a
superuser, which Immich expects.
Compose starts that container with 128 MB of shared memory. Cubeship cannot
size it, and Docker's default of 64 MB is too little for the large queries a
phone's first full sync runs; Postgres then fails them with could not resize shared memory segment. The Dockerfile adds
one setting, through a file the image's
configuration already includes: Postgres takes that memory from System V
shared memory, which that limit does not cover.
The image refuses to start when its data is not on a local Linux filesystem (ext4, xfs, btrfs or zfs). A volume is a directory on the server's own disk, which on a VPS is one of those.
What you are asked
| Input | What to give |
|---|---|
| Where Immich answers | A domain you control, pointed at your instance. The phone apps connect to it. |
| The password Immich uses for its database | Nothing — the instance generates it. |
After installing
- Open
https://<your domain>straight away. Immich has no default account: the first person to sign up becomes the admin. - In the Immich app on your phone, enter
https://<your domain>as the server URL and sign in.
Immich finds the machine learning app through IMMICH_MACHINE_LEARNING_URL on
the immich app. That is only the default of the setting under
Administration → Settings → Machine Learning: a URL saved there wins.
The first time a job needs a model, machine learning downloads it from Hugging Face, so the server needs to reach the internet. Until then smart search and faces show nothing, and a new library takes a while to index on a CPU.
Reaching the data
Cubeship has no console into an app. Anything that needs a shell is done over
SSH on the machine the app runs on, with docker exec. For example, to reset
the admin's password with Immich's own command:
docker exec -it $(docker ps -qf name=cubeship-immich-production-immich) \
immich-admin reset-admin-password
or to open the database:
docker exec -it $(docker ps -qf name=cubeship-immich-production-postgres) \
psql -U postgres immich
The internal names follow the project, environment and app names you install with; they are on each app's page in the dashboard.
The volumes
Each of the three apps runs as one copy on the machine its volume is on, and a deploy stops it for a few seconds. While the database or the server is stopped, the web app and the phone apps cannot reach Immich.
Uploads are the whole library. The /data volume holds every original
photo and video, plus thumbnails and transcoded videos, which add 10–20% on
top. Size the server's disk for it.
Back up both the database volume and /data: the database holds the albums,
people, faces and which file is which asset, and restoring one without the
other loses that match. A volume backup stops the app for the whole copy, and
copies every file to an S3 store outside the instance — for a large library,
schedule it for when nobody is uploading, and expect it to take as long as
copying the library does. The /cache volume holds only models, which machine
learning downloads again.
Immich also dumps its own database into /data/backups every night at 2:00
and keeps the last 14 (Administration → Settings → Backup), so a backup of
/data carries a recent copy of the database with it.
Resources
The server, machine learning and the database are each limited to 2 GiB of
memory; the server and machine learning to 2 CPUs, the database to 1. Immich
asks for at least 6 GB of memory and 2 cores for the whole stack, and
recommends 8 GB and 4 cores: a VPS with 8 GB is the smallest to install this
on. Machine learning unloads a model after five minutes unused, and raising
its limits is what helps a large first import.
Updating
Change both tags in template.yaml to the new release. Immich migrates its
database when the server starts, and there is no going back: back up the
database volume and /data first, and read the
release notes. Move the
database image in database/Dockerfile only to the one that release's
docker-compose.yml names, then release this repository and point the
postgres app's ref at the new release.
What this creates
postgres
https://github.com/cubeshipd/cubeship-immich-template
machine-learning
ghcr.io/immich-app/immich-machine-learning:v3.2.0
immich
ghcr.io/immich-app/immich-server:v3.2.0
immich-redis
Redis 7.4
/var/lib/postgresql/data
Volume of postgres
/cache
Volume of machine-learning
/data
Volume of immich
# yaml-language-server: $schema=https://cubeship.dev/schema/template/v1.json
version: 1
# The first release that keeps a volume's data across deploys.
minCubeship: "0.7.0"
project: immich
inputs:
- key: domain
type: domain
label: Where Immich answers
- key: dbPassword
type: secret
label: The password Immich uses for its database
generate: 32
databases:
- key: redis
name: immich-redis
engine: redis
version: "7.4"
apps:
- key: database
name: postgres
# Immich's own Postgres, with the VectorChord extension the managed one
# lacks. The Dockerfile adds one setting for the shared memory Cubeship
# cannot size.
repo: https://github.com/cubeshipd/cubeship-immich-template
ref: v1.0.0
build: dockerfile
dockerfile: database/Dockerfile
port: 5432
# No health: Postgres speaks no HTTP.
# No domain: only Immich talks to it.
volumes:
- path: /var/lib/postgresql/data
limits: { cpu: 1, memory: 2Gi }
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${input.dbPassword}
POSTGRES_DB: immich
POSTGRES_INITDB_ARGS: --data-checksums
- key: ml
name: machine-learning
image: ghcr.io/immich-app/immich-machine-learning
tag: v3.2.0
port: 3003
health: /ping
# The models it downloads on first use.
volumes:
- path: /cache
limits: { cpu: 2, memory: 2Gi }
- key: server
name: immich
image: ghcr.io/immich-app/immich-server
tag: v3.2.0
port: 2283
health: /api/server/ping
domains:
- host: ${input.domain}
volumes:
- path: /data
limits: { cpu: 2, memory: 2Gi }
env:
DB_HOSTNAME: ${app.database.internal}
DB_PORT: ${app.database.port}
DB_USERNAME: postgres
DB_PASSWORD: ${input.dbPassword}
DB_DATABASE_NAME: immich
# Not attached: Immich reads REDIS_HOSTNAME, and an attachment writes
# REDIS_HOST.
REDIS_HOSTNAME: ${db.redis.host}
REDIS_PORT: ${db.redis.port}
REDIS_PASSWORD: ${db.redis.password}
IMMICH_MACHINE_LEARNING_URL: http://${app.ml.internal}:${app.ml.port}