Put your photos without GPS back on the map — several independent clues, one map, your pick.
English · Deutsch · Français · Italiano · Русский
Important
Updating to 0.6 from 0.5 or older: one manual step, once.
Why. Up to 0.5, compose.yaml named the exact version of the app, so every update
also needed git pull. From 0.6 on, compose.yaml follows the major version and reads all
settings from .env: it stays the same, and every later update is just docker compose pull
and docker compose up -d.
How. In the folder of the app; your .env and your data stay as they are.
Without the worker:
curl -L -o compose.yaml https://github.com/Ivamic/immich-steward/releases/latest/download/compose.yaml
docker compose pull
docker compose up -dWith the worker on the server:
curl -L -o compose.yaml https://github.com/Ivamic/immich-steward/releases/latest/download/compose.yaml
docker compose --profile worker pull
docker compose --profile worker up -dIf .env has a line STEWARD_VERSION=…, remove it first: it would keep the old version. Details.
Status: under active development. An early version: reviewing photos without a location, suggestions from photos taken close in time, GeoCLIP and similar photos of your own library already work; the other modules are still to come. Expect bugs and changes between versions, and keep a backup of your Immich library before writing locations to it.
A self-hosted companion app for Immich. For each photo without a location, several modules — photos taken close in time, the GeoCLIP neural network and similar photos of the same place in your own library — suggest where it was taken, all on one map. You pick one with a click or a key, or set the place of many photos at once. Nothing is written to Immich until you do, and every change can be undone.
Typical use case: a family photo archive with scanned prints, old digital cameras without GPS, or messenger downloads that lost their metadata.
The app combines several independent location detection modules. Each module proposes one or more candidate locations, and all candidates are shown together on a map, color-coded by module and numbered by rank. You review them, pick one, or place a point manually.
Available modules:
- Time neighbors — photos taken shortly before and after that already have a location. The route between them is drawn on the map, colored by time; cameras with a wrong clock can be excluded, or only the photo's own camera kept.
- GeoCLIP (optional worker) — worldwide image geolocation by a neural network, running locally on the server CPU or on a GPU of another computer. Shown by confidence, with a gallery filter for its confident guesses. It can also look at a part of the photo only: a building behind a crowd, a sign, a mountain on the horizon.
- Similar photos (optional worker) — photos of the same place in your own library, with a location: the same street, house, view or room, from another angle or in another season. Uses MegaLoc, a model made for recognizing places; on a real family library, about half of its suggestions are right within 1 km, and more than eight out of ten of its confident ones. The search always includes the locations you have set since.
A photo can be rotated in the app for a better look, the modules run on it rotated, and the rotation can be saved to Immich.
To check a place by eye, satellite imagery can be shown under the map: Sentinel-2 by EOX (no key), Esri World Imagery or MapTiler Satellite (with your own key). All of them are off until you switch one on.
The gallery shows your photos by month and day and marks the ones with an easy answer: a photo with a location taken minutes before or after, a confident GeoCLIP guess, a similar photo with a location. Videos play in the gallery and in the photo card. Everything works on a phone too.
Groups gather the photos of one view by themselves, with a location or without: when the photos with a location agree on a spot, the place can be set on the others in one step, after a look at the map. A subject that moved over the years shows month by month, a spot that does not fit stands out, and photos that tell nothing of the place (a pet, a document) can be marked, so that they suggest nothing.
Planned:
- Street-level panoramas on the map, to check a place by eye.
- Geometric verification of similar photos (matching details, not only the overall look), and finding by itself the photos that show no place at all: close-up portraits, pets, plants, objects.
- LLM reasoning (Img2Loc-style) — a vision-language model reasons over the photo plus the evidence collected by the other modules.
- External services — optional cloud recognizers, one module per service.
New modules can be added later as plugins.
- Nothing is written to Immich automatically. Coordinates are sent only after you confirm a location. Every change can be undone.
- Local first. Default modules run on your own hardware. Modules that send images to third-party services are never run automatically — only on an explicit request, with a clear warning about what is sent and where.
- Region-based search. If the whole picture is misleading (for example, a crowd in front of a monument), select a part of the image and run detection on that region only.
- "Not applicable" flag. Mark photos where a location makes no sense, so they never show up in your to-do list again.
Steward runs with Docker Compose next to your Immich server (Immich v3.x):
mkdir immich-steward && cd immich-steward
curl -L -o compose.yaml https://github.com/Ivamic/immich-steward/releases/latest/download/compose.yaml
curl -L -o example.env https://github.com/Ivamic/immich-steward/releases/latest/download/example.env
cp example.env .env # set STEWARD_IMMICH_URL, STEWARD_SECRET_KEY and DB_PASSWORD
docker compose up -dThe images are pulled from GitHub Container Registry (ghcr.io/ivamic/immich-steward-core,
amd64 and arm64); they can also be
built from source. Updating is
docker compose pull && docker compose up -d.
Then open http://<your-server>:8780 and sign in with your Immich account.
GeoCLIP and similar photos run in an optional worker container (2 to 4 GB of RAM with both
models loaded): set STEWARD_WORKER_TOKEN in .env and start with
docker compose --profile worker up -d.
Workers can also run on other computers in your network: Processing → Add a device shows
the commands to copy (more).
The documentation explains it all step by step: installation, using the app, workers, configuration, updating, privacy and troubleshooting.
- Backend: Python (FastAPI), PostgreSQL with pgvector.
- Runs with Docker Compose next to your Immich server.
- ML modules run in workers: separate processes that take jobs from the core over HTTP, on the server CPU or on another computer with a GPU. Workers never see an Immich API key: the core gives them the photos.
- Web UI: timeline gallery with filters, photo view with a map, keyboard-driven review.
- UI in English, German, French, Italian and Russian.
Repository layout:
| Path | Contents |
|---|---|
core/ |
Core service: Python package steward (FastAPI), Alembic migrations, tests |
worker/ |
Worker: Python package steward_worker that runs ML location modules (GeoCLIP, similar photos) |
frontend/ |
Web UI: SvelteKit single-page app; translations in frontend/messages/ |
docker/ |
Dockerfiles of the core image (with the built web UI) and of the worker image (CPU); .github/workflows/release.yml publishes them for every version tag |
compose.yaml |
Core service, PostgreSQL with pgvector, optional worker (profile worker); attached to every release |
compose.build.yaml |
Builds the images from this source instead of pulling them |
docs/ |
User documentation, starting at docs/README.md |
Requirements: Python 3.12 with uv, Node.js 24 with pnpm, Docker. Run all commands from the repository root unless stated otherwise.
cp .env.example .env # then set DB_PASSWORD and STEWARD_DATABASE_URL
# Database only, exposed on localhost
docker compose -f compose.yaml -f compose.dev.yaml up -d db
# Core service on http://127.0.0.1:8080
uv sync
uv run alembic -c core/alembic.ini upgrade head
uv run python -m steward --reload
# Web UI dev server on http://localhost:5173 (proxies /api to the core)
cd frontend && pnpm install && pnpm dev
# Optional: a worker with GeoCLIP and similar photos (PyTorch for the CPU; --extra cuda for an
# NVIDIA GPU). The core needs the same STEWARD_WORKER_TOKEN in .env.
uv sync --all-packages --extra geoclip --extra similar --extra cpu
STEWARD_WORKER_TOKEN=... uv run python -m steward_worker --core http://127.0.0.1:8080 --modules similar,geoclipChecks (the same as in CI):
uv run ruff check . && uv run ruff format --check . && uv run mypy && uv run pytest
cd frontend && pnpm check && pnpm lint && pnpm test && pnpm check:licensesDatabase integration tests run when STEWARD_TEST_DATABASE_URL is set.
Full stack in Docker (UI on http://localhost:8780 by default):
docker compose -f compose.yaml -f compose.build.yaml up -d --buildUI strings live in frontend/messages/<locale>.json; English (en.json) is the source.
A new string must be added to every locale file at once — a test checks that all locales
have the same keys and placeholders.
The German, French and Italian translations were made with the help of AI and have not been reviewed by native speakers yet. Corrections are very welcome: open a pull request against the locale file or an issue with the wrong wording.
The README is translated too (README.<locale>.md), without the development sections. The
English README is the reference: a translation is outdated when README.md was changed in a
later commit than the translation.
- Bugs — open an issue with the versions of Steward and Immich and the steps to reproduce.
- Ideas and questions — start a thread in Discussions. Stories of how you use the app and which photos it fails on are welcome too.
- Pull requests are welcome. For anything bigger than a small fix, please discuss it in an issue or a discussion first, so that your work fits the plans of the project.
- Security issues — please report them privately, see SECURITY.md.
Steward is free and developed in spare time. If it saved you hours of manual tagging, you can support its development with a donation in cryptocurrency. Starring the repository, reporting bugs and sharing the project help too.
| Currency | Network | Address |
|---|---|---|
| USDT | TRON (TRC-20) | TAqGEvmDTw2ro4BKQmcPPtqK5xEMfNaiSX |
| ETH, USDT and other tokens | Ethereum and EVM networks (Base, Polygon, Arbitrum…) | 0x783C192d69fE5E9DAfa1dC2c41FeE272122AE031 |
| SOL | Solana | 38gidbL2ocv5c6CK7zP7c3QWVHisYyqhnrMV3tuaYpgp |
| BTC | Bitcoin | bc1qsdys5hvx7vlxp25mz77hfgz2l35x8klc8z2npx |
Please double-check the network before sending: funds sent over a different network may be lost.
Copyright (C) 2026 Ivamic.
GNU Affero General Public License v3.0 or later (AGPL-3.0-or-later).
Name and logo. The name "Steward for Immich" and its logo (docs/images/logo.svg,
frontend/src/lib/assets/favicon.svg and the mark drawn in frontend/src/lib/ui/Brand.svelte)
are not licensed under the AGPL; all rights are reserved by the author. You may use them to refer
to this project. Forks and modified versions must use a different name and logo.
This project is developed with the help of AI tools (Claude by Anthropic). All code is reviewed and maintained by a human author.
This is an independent community project. It is not affiliated with or endorsed by the Immich project or FUTO.




