How Immich Ensures Data Integrity and Security via StorageService
Immich guarantees data integrity and security by validating storage mount points at startup, verifying read/write permissions through hidden marker files, automatically migrating media paths when locations change, and executing resilient background deletion jobs—all orchestrated through the centralized StorageService.
The StorageService in the immich-app/immich repository serves as the trust anchor for the entire media pipeline. Located at server/src/services/storage.service.ts, this service implements a defense-in-depth strategy that combines environment-driven configuration with runtime verification to prevent data loss and permission errors before the system accepts user requests.
Detecting and Configuring the Media Location
When the Immich server boots, StorageService determines the canonical media root through a cascading detection strategy implemented in the detectMediaLocation method.
Environment-Driven Configuration
The service first checks for the IMMICH_STORAGE_MEDIA_LOCATION environment variable (lines 23–28). If present, this path is immediately adopted as the authoritative media location:
# .env
IMMICH_STORAGE_MEDIA_LOCATION=/custom/media/path
Fallback Directory Probing
If the environment variable is absent, the service probes a hardcoded list of candidate directories—/data and /usr/src/app/upload—selecting the first existing path (lines 30–40). The resolved location is then propagated globally via StorageCore.setMediaLocation() invoked during onBootstrap (line 48), ensuring all downstream services reference the same base path.
Verifying Mount-Folder Integrity at Runtime
Immich verifies that every required storage folder remains present and writable on every startup. This prevents scenarios where Docker volumes fail to mount or permission changes render media directories read-only.
The Hidden Mount File Mechanism
For each folder defined in the StorageFolder enum (enumerated at line 65), the service creates a hidden .immich file via createMountFile() (lines 68–69). This marker file serves as proof that the process can both create files in the directory and subsequently locate them.
Explicit Read and Write Verification
The service performs two distinct validation checks for every folder:
- Read verification:
verifyReadAccess()attempts to read the hidden.immichfile (line 71). Failure throws anImmichStartupErrorwith a detailed message pointing to Immich documentation. - Write verification:
verifyWriteAccess()overwrites the marker file with a current timestamp (line 72), confirming the process retains write permissions.
If either check fails, the service emits an error referencing the docsMessage constant (line 19) to guide administrators toward remediation:
throw new ImmichStartupError(`Failed to read: "${externalPath} (${internalPath}) - ${docsMessage}"`);
Persistent Check Results
Successful validations are persisted to the systemMetadataRepository (lines 80–83), storing flags under mountChecks to avoid redundant work on subsequent boots. For environments where these checks must be bypassed—such as specific NAS configurations—operators can set storage.ignoreMountCheckErrors (lines 87–93), though this constitutes an explicit opt-in security trade-off.
Automatic Media Location Migration
When administrators relocate the media storage mount point, StorageService detects the drift and orchestrates an atomic database migration to maintain referential integrity.
Detection via Asset Sampling
During onBootstrap, the service retrieves sample assets via assetRepository.getFileSamples() (lines 99–104) to infer the current filesystem location. If the detected path diverges from the previously stored MediaLocation, the service validates that all existing files still reside under the previous root.
Atomic Path Rewriting
Once validated, the service invokes databaseRepository.migrateFilePaths(previous, current) (line 124) to rewrite every file path record in the database. This ensures that database entries remain synchronized with the physical file locations, preventing broken asset links and orphaned records.
Resilient File Deletion and Cleanup
The background job FileDelete (handled by handleDeleteFiles) removes orphaned files safely without risking system stability.
The method iterates over the supplied file list (line 40), wrapping each deletion attempt in a try…catch block (lines 45–49). This design ensures that a single corrupt path or permission error logs a warning but does not abort the entire cleanup job, preventing denial-of-service conditions caused by malformed filenames or transient filesystem issues.
Security-First Design Patterns
The StorageService implements several security-focused architectural decisions:
- Mount point proof via
.immichfiles: Guarantees that a directory is not only reachable but owned by the Immich process, detecting volume mount failures immediately. - Startup-time permission verification: Catches permission misconfigurations before the API begins serving requests, reducing the attack surface for privilege escalation.
- Graceful degradation options: The
storage.ignoreMountCheckErrorsflag provides a controlled escape hatch for exotic storage configurations while requiring explicit operator acknowledgment of risk. - Centralized error documentation: All mount errors include documentation links, ensuring rapid remediation and reducing mean-time-to-repair for configuration errors.
Summary
- Centralized detection:
detectMediaLocationresolves the media root from environment variables or fallback paths, storing it inStorageCorefor global access. - Runtime verification: Every startup validates all
StorageFolderdirectories via hidden.immichmarker files, confirming read and write permissions throughverifyReadAccessandverifyWriteAccess. - Automatic migration: When storage locations change,
migrateFilePathsatomically updates database records to match the new filesystem layout. - Fault-tolerant cleanup: The
handleDeleteFilesbackground job logs individual deletion failures without halting the entire cleanup process. - Explicit security trade-offs: The
ignoreMountCheckErrorsconfiguration allows intentional bypassing of mount checks only when operators explicitly accept the risk.
Frequently Asked Questions
How does Immich detect if a storage folder is properly mounted?
Immich creates a hidden .immich file in each required folder during startup using createMountFile(), then attempts to read and overwrite this file via verifyReadAccess() and verifyWriteAccess(). If either operation fails, the service throws an ImmichStartupError and halts initialization to prevent operations on potentially unmounted or read-only volumes.
What happens if I move my Immich media directory to a new location?
When StorageService detects a mismatch between the configured MediaLocation and the actual filesystem paths found in asset samples, it triggers databaseRepository.migrateFilePaths(). This updates all database records from the old path prefix to the new one, ensuring asset links remain valid after the move.
Can I disable the mount verification checks?
Yes, by setting storage.ignoreMountCheckErrors in your environment configuration. However, this is an explicit opt-in that bypasses the startup-time integrity checks in onBootstrap (lines 87–93). Disabling these checks is only recommended for specific NAS or bind-mount configurations where the verification logic produces false negatives.
How does Immich handle errors during background file deletion?
The handleDeleteFiles method processes deletions inside individual try…catch blocks for each file. This ensures that a single corrupted filename, permission error, or missing file logs a warning without aborting the entire batch, maintaining system availability during cleanup operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →