fcandi61 downloadsInsert photos taken on a daily note's date from a self-hosted Immich server, resized, captioned, and linked back to Immich.
An Obsidian plugin that inserts photos from your self-hosted Immich server into your daily notes — resized, captioned, and linked back to Immich.
Status: early development. Not yet released as a Community Plugin. Install via BRAT (see below) to try it now.

{{description}}, {{time}}, {{city}}, {{country}}, {{camera}}, {{people}},
{{filename}}, …); an empty rendered caption is omitted entirely, no dangling
formattingasset.read and asset.view permissionsasset.read and asset.view. A key that is scoped too narrowly will authenticate
fine but fail with 403 Forbidden as soon as the plugin tries to load a
thumbnail — the built-in "Test connection" button in the plugin settings checks a
real thumbnail fetch, not just a lightweight ping, specifically to catch this.https://immich.example.com (no trailing slash)| Result | Meaning |
|---|---|
| Success | Server reachable, key valid, thumbnail fetch works |
| Network error | Server URL unreachable — check the URL, and on mobile check your VPN connection |
| Unauthorized (401) | API key missing or invalid |
| Forbidden (403) | Key is valid but lacks a required scope — recreate it with asset.read + asset.view |
| Group | Setting | Default | Notes |
|---|---|---|---|
| Connection | Server URL | (empty) | e.g. https://immich.example.com |
| API key | (empty) | Stored in plain text in data.json, per Obsidian's standard plugin-data behavior |
|
| Test connection | — | Verifies reachability, auth, and a real thumbnail fetch | |
| Insert | Max edge length (px) | 800 |
Longest side of the inserted image; portrait and landscape scaled the same way; never upscales |
| JPEG quality | 0.85 |
Applied when re-encoding the downscaled image | |
| Attachment folder | attachments/immich/{{year}}/ |
Supports date placeholders | |
| File name template | {{date}}_immich_{{assetIdShort}} |
Includes the asset ID so re-inserting the same photo reuses the existing file instead of duplicating it | |
| Markdown template | []({{immichUrl}})*{{caption}}* |
Full control over the inserted block | |
| Caption template | {{description}} |
Empty rendered result → caption line omitted | |
| Embed style | Markdown | Markdown link (clickable) or wikilink | |
| Link to Immich | On | Wraps the image in a link back to the asset in Immich | |
| Daily note | Title date format | YYYY-MM-DD |
Moment.js format used to parse the note title |
| Frontmatter fallback field | (empty) | Used if the title doesn't parse, e.g. created |
|
| Display | Grid columns | 3 |
Applies on desktop and mobile alike |
| Language | Auto | Follows Obsidian's locale; can be forced to English, German, Spanish, French, Japanese, or Chinese |
The most common self-hosted Immich setup is a server reachable only inside a private network or VPN (Tailscale, WireGuard, etc.), not on the public internet. Immich Journal works well with this:
requestUrl() API, so there is no CORS
configuration to worry about on the Immich side, on desktop or mobile.Not yet available in the Community Plugins directory. Until it passes community review, install it via BRAT (Beta Reviewer's Auto-update Tool):
https://github.com/fcandi/obsidian-immich-journalImmich Journal deliberately uses only three stable, long-standing Immich API endpoints, to stay resilient against the fast pace of Immich's development:
POST /api/search/metadata — find photos taken on a given dayGET /api/assets/{id}/thumbnail?size=thumbnail|preview — load grid thumbnails and
the higher-resolution image used for the resized insert{server}/photos/{assetId} — no API call, just a URLIf a future Immich release changes these, please open an issue with your server version.
npm run dev — esbuild in watch mode, emits main.js into the repo root on every
change.
One-time setup: symlink this repo into your test vault's plugins folder, e.g.:
ln -s ~/DEV/obsidian-immich-journal "<vault>/.obsidian/plugins/immich-journal"
Enable the plugin in Obsidian, then reload Obsidian (or use a hot-reload plugin)
after each change to pick up the new main.js.
npm run build — production build.
npx tsc --noEmit — type-check without emitting.
npx vitest run — run the unit test suite.