Immich Stack
repository·main·Indexed 42 days ago
https://github.com/majorfi/immich-stackA tool for the Immich photo management system that automatically groups similar photos into stacks to organize burst sequences and similar shots. It includes a CLI for processing stacks, identifying duplicate assets based on filename and timestamp, and maintaining trash consistency via the fix-trash command, which can also remove orphaned RAW files.
What's inside Immich Stack
- Immich Stack is a Go-based CLI and library designed to group similar photos into stacks within an Immich instance. It automates the organization of photo collections by identifying patterns like burst sequences, duplicates, and similar captures. Users can configure grouping criteria, sort order, and parent selection using either environment variables or CLI flags.
Understand Stacker logging output
mainThe stacker provides colorized, structured output for monitoring operations. Logs include:
- Action status: Whether a stack was
created,updated, orskipped. - Error context: Errors include the specific asset or stack context that caused the failure.
- Progress: Counters are displayed during long-running operations.
- Action status: Whether a stack was
Handle Large Library 5xx Errors on GET /stacks
mainOn libraries with >100k stacks, the Immich
GET /stacksendpoint may return an HTTP 500 error because the JSON response exceeds Node.js's max string length.Automatic Fallback:
immich-stackautomatically detects this and switches to a per-asset hybrid lookup. This bypasses the failing endpoint by enumerating asset IDs and performing individual lookups.Performance: This fallback is slower. Expect execution time to be roughly
total_assets / 800seconds (with default concurrency of 10). For 450k assets, this is approximately 10 minutes.Note: A 4xx error (401, 403, 404) is not caused by this bug; it indicates an issue with your API key or URL and will not trigger a fallback.
Understand the Immich Stack CLI architecture
mainImmich Stack is built using the Cobra framework. It operates as a multi-command CLI tool where subcommands are defined in the
cmd/directory. The hierarchy starts from theimmich-stackroot command, which includes several subcommands likestack(the default),duplicates,fix-trash,help, andversion.Key architectural components include:
cmd/main.go: The entry point and root command setup.cmd/config.go: Handles shared configuration and environment loading.cmd/common.go: Contains helpers shared across all subcommands.pkg/immich: The core package used for client interactions.
Build complex logical expressions with AND, OR, and NOT
mainThe Advanced Expression format allows for recursive logical trees. Each node in the tree is either a Criteria Node (a leaf containing a specific key/match) or an Operator Node (a branch containing children).
Supported Operators:
AND: All children must match. Requires 1 or more children.OR: At least one child must match. Requires 1 or more children.NOT: The child must NOT match. Requires exactly 1 child.
{ "operator": "AND", "children": [ { "operator": "OR", "children": [ { "criteria": { "key": "originalFileName", "regex": { "key": "PXL_", "index": 0 } } }, { "criteria": { "key": "originalFileName", "regex": { "key": "IMG_", "index": 0 } } } ] }, { "operator": "NOT", "children": [{ "criteria": { "key": "isArchived" } }] } ] }How configuration precedence is handled
mainImmich Stack uses
viperto manage configuration. The precedence order is:- Command-line flags (Highest precedence)
- Environment variables
For example, if
API_KEYis set in the environment but--api-keyis also provided as a flag, the flag value will be used.Use Advanced Groups Format for OR logic
mainThe Advanced Groups Format allows for more complex logic using
mode: "advanced"andgroups.Key Behavior: Union Semantics In this mode,
ORgroups use union semantics. Assets are grouped if they share ANY matching criteria from the OR group. This creates connected components where assets sharing any criteria are linked together.Example: Group by Directory OR Timestamp This configuration groups assets that share either the same directory (extracted via split) OR fall within the same 1-second time window.
{ "mode": "advanced", "groups": [ { "operator": "OR", "criteria": [ { "key": "originalPath", "split": { "delimiters": ["/"], "index": 2 } }, { "key": "localDateTime", "delta": { "milliseconds": 1000 } } ] } ] }Manage memory usage based on library size
mainMemory usage scales with asset count (~1KB per asset), criteria complexity, and stack size. Choose your grouping mode based on your library size to stay within recommended memory bounds:
Library Size Recommended Mode Expected Memory < 10,000 assets Any mode < 100MB 10,000-50,000 assets Legacy or Groups 100-500MB 50,000-100,000 assets Legacy preferred 500MB-1GB > 100,000 assets Legacy only > 1GB Strategies for large libraries (>100k assets):
- Use Legacy mode with simpler criteria.
- Process in batches using
WITH_ARCHIVEDandWITH_DELETEDfilters. - Run the stacker during off-peak hours.
Optimize performance for Expression Mode nesting
mainWhen using
advancedmode with complex expressions, deep nesting increases evaluation costs. To maintain performance, follow these nesting level guidelines:- 1-2 levels: Negligible impact; safe for all use cases.
- 3-4 levels: Slight increase; acceptable for most libraries.
- 5+ levels: Noticeable impact; consider simplifying your criteria.
Each additional level of nesting multiplies the evaluation cost for every asset in the library.
{ "mode": "advanced", "expression": { "operator": "AND", "children": [ { "operator": "OR", "children": [ { "operator": "AND", "children": [ { "criteria": { "key": "originalFileName", "regex": { "key": "PXL_", "index": 0 } } }, { "criteria": { "key": "localDateTime", "delta": { "milliseconds": 1000 } } } ] } ] } ] } }Security Best Practices for Immich Stack
mainWhen deploying Immich Stack, follow these security recommendations:
- Use Docker Secrets: Instead of passing the
API_KEYdirectly in the.envfile, useAPI_KEY_FILEand point it to a mounted secret (e.g.,API_KEY_FILE=/run/secrets/immich_api_key). - Principle of Least Privilege: Restrict container capabilities and run as a non-root user.
- Version Pinning: Use specific image tags in production instead of
:latestto ensure stability.
- Use Docker Secrets: Instead of passing the
Configure Immich Stack Grouping Modes
mainImmich Stack uses three grouping modes to determine how assets are clustered into stacks. You can configure these via the
CRITERIAenvironment variable or the--criteriaflag.- Legacy Mode (Default): Groups by base filename (before extension) and local capture time. All criteria in the provided JSON array must match (AND logic).
- Advanced Groups Mode: Uses multiple grouping strategies, where each group has its own AND/OR operator. Configured as an object with
"mode": "advanced"and agroupsarray. - Advanced Expression Mode: Supports nested logical expressions with AND, OR, and NOT (unlimited nesting). Configured as an object with
"mode": "advanced"and anexpressiontree.
How Cron Mode execution and timing works
mainCron mode operates on a stateless execution loop. Each run is independent: it fetches fresh data from the Immich API, recalculates groupings from scratch, and makes stacking decisions without remembering previous runs.
Execution Loop:
[Start] → [Run Stacker] → [Wait CRON_INTERVAL] → [Run Stacker] → [Wait] → ...Timing Behavior:
- The interval timer starts after each run completes, not from the start time.
- If a run takes longer than the
CRON_INTERVAL, the next run starts immediately after completion (no runs are skipped). - A warning is logged if processing time exceeds 50% of the interval.
Recommendation: Set
CRON_INTERVALto at least 2× your expected processing time to avoid overlapping runs.