ImmichFrame turns any screen into a digital photo frame fed by your own Immich server. An old tablet, a spare monitor on a Raspberry Pi or a smart TV browser can cycle through your albums, favourites, memories or photos of specific people, with an optional clock, weather and calendar on top. No cloud photo frame subscription required.
In this guide we will run ImmichFrame in Docker using the official image, connect it to Immich with an API key, and configure it from its built-in admin page.
ghcr.io/immichframe/immichframe. This version added the admin UI at /admin and stores settings in a database; settings are no longer read from environment variables.✅ Before you start
- A Linux host with Docker and Docker Compose v2 installed (
docker compose versionshould work). Usedocker composewith a space: the old Pythondocker-composev1 can crash on modern Docker. - A working Immich server (see my Immich guide) and an account on it.
- One free port,
8080, for the frame and admin UI. - A screen to show it on: any device with a web browser, or the ImmichFrame apps for Android, Apple and Frameo devices.
STEP 1: User and group IDs (and your secrets)
ImmichFrame does not use PUID/PGID. The container always runs as user 1000, so its config folder must belong to that user (we do that in STEP 2).
You need two secrets. First an admin password for the ImmichFrame admin page:
openssl rand -base64 18Keep them somewhere safe, such as your password manager. In the examples below they appear as placeholders like CHANGE_ME_STRONG_PASSWORD and change-me.
Second, an Immich API key: in Immich, open your avatar → Account Settings → API Keys → New API Key, name it ImmichFrame and copy it. Tip: create a separate Immich user just for the frame and share the albums you want to show with it, so the frame can only see what you choose.
STEP 2: Create the folders
I keep every app's files under one parent folder so a backup is a single job. Create the folders for ImmichFrame:
mkdir -p /path/to/appdata/immich-frame/config
sudo chown -R 1000:1000 /path/to/appdata/immich-frame/configThe container stops at startup with Cannot open the settings database if it cannot write to this folder, which is why the chown is there.
When you are done, the layout should look like this:
/path/to/appdata/immich-frame/
docker-compose.yml
config/ # immichframe.db (settings) and optional Settings.ymlFolder layout
Finally, I give every web app that sits behind my reverse proxy one shared Docker network called proxy. ImmichFrame joins it so Traefik or Nginx Proxy Manager can reach it by container name, while its database and helpers stay on the stack's own private network. Create it once (if it already exists, Docker just tells you so):
docker network create proxySTEP 3: The compose file
Create /path/to/appdata/immich-frame/docker-compose.yml and paste in the following. It is the official compose example with an absolute path, your time zone and the shared proxy network added. Use the copy button in the top right corner of the block.
name: immichframe
services:
immichframe:
container_name: immichframe
image: ghcr.io/immichframe/immichframe:latest
restart: on-failure
volumes:
- /path/to/appdata/immich-frame/config:/app/Config
ports:
- "8080:8080"
environment:
TZ: "Europe/London"
IMMICHFRAME_ADMIN_PASSWORD: "CHANGE_ME_STRONG_PASSWORD" # admin password from STEP 1
networks:
- proxy
networks:
proxy:
external: truedocker-compose.yml
Optionally, you can pre-load the Immich connection with a minimal Settings.yml in the config folder. It is imported once, on the very first start; after that the admin UI is the source of truth:
General:
Interval: 45 # seconds per photo
Layout: 'splitview' # 'single' or 'splitview' (two portrait photos side by side)
UnitSystem: 'metric'
Accounts:
- ImmichServerUrl: 'http://192.168.1.20:2283'
ApiKey: 'change-me' # the Immich API key from STEP 1/path/to/appdata/immich-frame/config/Settings.yml (optional)
STEP 4: The settings explained
| Setting | What it does |
|---|---|
/app/Config |
Holds immichframe.db, the SQLite database with every setting, plus an optional Settings.yml/Settings.json for the first start. Must be writable by user 1000. Back it up. |
8080:8080 |
The frame itself (/) and the admin UI (/admin). |
TZ |
Your time zone, for the clock and photo dates. List of zones. |
IMMICHFRAME_ADMIN_PASSWORD |
The admin page password. Optional (without it, a new install asks you to choose one), but setting it is also how you get back in if you ever lock yourself out: the variable always wins. |
restart: on-failure |
As in the official example: restart only if it crashes. |
ImmichServerUrl / ApiKey |
Where your Immich server is and how to log in. The only two settings you really need. Use the IP or a container name on a shared network, never localhost. |
STEP 5: Start ImmichFrame
From the folder containing your compose file:
cd /path/to/appdata/immich-frame
docker compose up -d
docker compose logs -fWait until the logs settle down (press Ctrl+C to stop following them; the container keeps running), then check it is up:
docker ps --filter name=immich-frameYou should see immichframe listed as Up. If it cannot reach Immich, it now logs a warning instead of exiting, so you can fix the account in the admin UI.
STEP 6: First-run setup
- Open
http://192.168.1.20:8080/adminand log in with your admin password. - Under accounts, add your Immich server URL (for example
http://192.168.1.20:2283) and the API key, unless you already did that inSettings.yml. - Choose what to show: specific albums, people, favourites, memories or recent days. Then tweak the display: interval, transitions, clock, photo date and location, and the
splitviewlayout. - Optionally add an OpenWeatherMap API key and your coordinates for weather, or an
.icscalendar URL. - Open
http://192.168.1.20:8080/on the screen you want to use as a frame. Changes from the admin page apply live, with no restart.
Add
?client=livingroom (or any name) to the frame URL on each device so each one moves through your photos independently.STEP 7: Put ImmichFrame behind a reverse proxy
A proxy is optional if your frames are all at home, but it gives you HTTPS for the admin page. Use a dedicated hostname such as frame.example.com.
Option A: Traefik (Docker labels)
If Traefik runs on the same Docker host and is attached to the proxy network, add these labels to the immichframe service. They assume your HTTPS entry point is called websecure and your certificate resolver is called cloudflare; change them to match your Traefik config.
labels:
- traefik.enable=true
- traefik.http.routers.immichframe.rule=Host(`frame.example.com`)
- traefik.http.routers.immichframe.entrypoints=websecure
- traefik.http.routers.immichframe.tls.certresolver=cloudflare
- traefik.http.services.immichframe.loadbalancer.server.port=8080docker-compose.yml (extra lines)
Option B: Traefik (file provider)
If Traefik runs on a different machine, add a router and service to your dynamic config file instead:
http:
routers:
immichframe:
rule: Host(`frame.example.com`)
entryPoints:
- websecure
tls:
certResolver: cloudflare
service: immichframe
services:
immichframe:
loadBalancer:
servers:
- url: http://192.168.1.20:8080dynamic.yml
Option C: Nginx Proxy Manager
Add a Proxy Host for frame.example.com with scheme http, forward hostname immichframe (if NPM is on the proxy network) or 192.168.1.20, and forward port 8080. Request a certificate on the SSL tab and tick Force SSL.
AuthenticationSecret under General in the admin UI. Every client then has to authenticate with that secret as a Bearer token.Updating ImmichFrame
The latest tag follows the newest release. To update:
cd /path/to/appdata/immich-frame
docker compose pull
docker compose up -d
docker image prune -fconfig folder. v1.0.38 was a breaking release: if you are coming from an older version that used environment variables for settings, you need to re-enter them in the admin UI.Troubleshooting
Cannot open the settings database in '/app/Config'. The config folder is not writable by user 1000. Run sudo chown -R 1000:1000 /path/to/appdata/immich-frame/config and restart.
Changes to Settings.yml do nothing. That is expected: the file is only imported on the first start. Use the admin UI.
The frame shows no photos. The API key is wrong, the Immich user cannot see the albums you picked, or the URL is unreachable from the container. Check docker logs immichframe.
Locked out of /admin. Set IMMICHFRAME_ADMIN_PASSWORD in the compose file and run docker compose up -d.
port is already allocated. 8080 is a popular port. Change the left-hand side, for example "8090:8080".
That's it! That drawer of old tablets just became a set of photo frames. 🖼️