Support Immich URL/API key via docker-compose env vars
Build and push server image / build-and-push (push) Successful in 33s

docker-compose.yml is tracked in a repo meant for publishing, so it can't
hold a real API key. Renamed it to docker-compose.yml.example (placeholder
values, safe to commit) and gitignored the real docker-compose.yml --
deploying is now "cp the example, fill in real values, docker compose up",
no .env file needed.

config.load() now reads IMMICH_URL/IMMICH_API_KEY from the environment
and applies them on top of whatever's in config.json, so setting them in
the compose file's environment: block takes effect without ever touching
the web UI. Env vars always win over the UI-saved values when both are
present -- verified they survive a save() with different UI-entered
values still in place.
This commit is contained in:
2026-07-18 16:05:06 -04:00
parent 2cd7283ce9
commit e7f096ac23
4 changed files with 40 additions and 10 deletions
+3
View File
@@ -11,6 +11,9 @@ server/**/__pycache__/
server/.venv/ server/.venv/
server/*.egg-info/ server/*.egg-info/
server/data/ server/data/
# Real deploy config, copied from docker-compose.yml.example -- holds the
# Immich API key, must never be committed.
server/docker-compose.yml
.env .env
# Editor / OS cruft # Editor / OS cruft
+20 -8
View File
@@ -10,13 +10,22 @@ algorithm itself -- it just streams the response straight to the panel.
1. **Get an Immich API key**: in Immich, go to Account Settings -> API Keys 1. **Get an Immich API key**: in Immich, go to Account Settings -> API Keys
-> New API Key. Read-only access to albums/assets is enough. -> New API Key. Read-only access to albums/assets is enough.
2. **Run the server**: 2. **Copy the compose file and fill in your Immich details**:
```
cp docker-compose.yml.example docker-compose.yml
```
Edit `docker-compose.yml` and set `IMMICH_URL`/`IMMICH_API_KEY` under
`environment:`. `docker-compose.yml` is gitignored (it'll hold your real
API key) -- `docker-compose.yml.example` is the one that's committed.
3. **Run the server**:
``` ```
docker compose up -d docker compose up -d
``` ```
3. Open `http://<this-machine>:8420/` in a browser, enter your Immich URL 4. Open `http://<this-machine>:8420/` in a browser, click **Load Albums**,
and API key, click **Load Albums**, pick one, and **Save**. pick one, and **Save**. (The Immich URL/API key fields will already be
4. On the ESP32's captive portal setup form, set the **Tools Server** field populated from the environment; changing them in the UI has no effect
as long as the env vars are set -- they win on every load.)
5. On the ESP32's captive portal setup form, set the **Tools Server** field
to `<this-machine>:8420`. to `<this-machine>:8420`.
## Endpoints ## Endpoints
@@ -31,8 +40,10 @@ algorithm itself -- it just streams the response straight to the panel.
## Notes ## Notes
- Config (including the Immich API key) is stored in `./data/config.json` - Album/order/refresh-interval/etc. are stored in `./data/config.json` on
on the host via the compose volume mount. the host via the compose volume mount. Immich URL/API key are too if set
via the web UI, but `IMMICH_URL`/`IMMICH_API_KEY` env vars (see Setup
above) always take precedence when present.
- `/frame/image` isn't authenticated yet. That's fine on a trusted home - `/frame/image` isn't authenticated yet. That's fine on a trusted home
LAN for now, but worth revisiting once the ESP32 side is wired up to LAN for now, but worth revisiting once the ESP32 side is wired up to
send a shared device token. send a shared device token.
@@ -47,8 +58,9 @@ Every push to `main` that touches `server/` triggers a Gitea Actions
workflow (`.gitea/workflows/server-docker-build.yml`) that builds this workflow (`.gitea/workflows/server-docker-build.yml`) that builds this
image and pushes it to this repo's Gitea Container Registry at image and pushes it to this repo's Gitea Container Registry at
`git.thumeit.com/tfaour/espresso-frame-server`. `docker-compose.yml` `git.thumeit.com/tfaour/espresso-frame-server`. `docker-compose.yml`
already points at that image, so a deploy host doesn't need this repo's (copied from `docker-compose.yml.example`, see Setup above) already
build context at all -- just the compose file: points at that image, so a deploy host doesn't need this repo's build
context at all -- just the compose file:
``` ```
docker compose pull docker compose pull
+15 -2
View File
@@ -28,8 +28,21 @@ class FrameConfig(BaseModel):
def load() -> FrameConfig: def load() -> FrameConfig:
with _lock: with _lock:
if not CONFIG_PATH.exists(): if not CONFIG_PATH.exists():
return FrameConfig() cfg = FrameConfig()
return FrameConfig(**json.loads(CONFIG_PATH.read_text())) else:
cfg = FrameConfig(**json.loads(CONFIG_PATH.read_text()))
# IMMICH_URL/IMMICH_API_KEY set in the environment (e.g. docker-compose.yml,
# see docker-compose.yml.example) take precedence over whatever's saved
# in CONFIG_PATH, so credentials never need to go through the web UI.
env_url = os.environ.get("IMMICH_URL")
env_key = os.environ.get("IMMICH_API_KEY")
if env_url:
cfg.immich_url = env_url
if env_key:
cfg.immich_api_key = env_key
return cfg
def save(cfg: FrameConfig) -> None: def save(cfg: FrameConfig) -> None:
@@ -8,4 +8,6 @@ services:
- ./data:/data - ./data:/data
environment: environment:
- CONFIG_PATH=/data/config.json - CONFIG_PATH=/data/config.json
- IMMICH_URL=http://your-immich-host:2283
- IMMICH_API_KEY=your-immich-api-key-here
restart: unless-stopped restart: unless-stopped