Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"@types/lodash-es": "^4.17.12",
"@types/micromatch": "^4.0.9",
"@types/mock-fs": "^4.13.1",
"@types/node": "^24.10.3",
"@types/node": "^24.10.4",
"@vitest/coverage-v8": "^3.0.0",
"byte-size": "^9.0.0",
"cli-progress": "^3.12.0",
Expand All @@ -36,7 +36,7 @@
"typescript": "^5.3.3",
"typescript-eslint": "^8.28.0",
"vite": "^7.0.0",
"vite-tsconfig-paths": "^5.0.0",
"vite-tsconfig-paths": "^6.0.0",
"vitest": "^3.0.0",
"vitest-fetch-mock": "^0.4.0",
"yaml": "^2.3.1"
Expand Down
4 changes: 3 additions & 1 deletion docker/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ services:

redis:
container_name: immich_redis
image: docker.io/valkey/valkey:9@sha256:fb8d272e529ea567b9bf1302245796f21a2672b8368ca3fcb938ac334e613c8f
image: docker.io/valkey/valkey:9@sha256:546304417feac0874c3dd576e0952c6bb8f06bb4093ea0c9ca303c73cf458f63
healthcheck:
test: redis-cli ping || exit 1

Expand All @@ -146,6 +146,8 @@ services:
ports:
- 5432:5432
shm_size: 128mb
healthcheck:
disable: false
# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
# immich-prometheus:
# container_name: immich_prometheus
Expand Down
8 changes: 5 additions & 3 deletions docker/docker-compose.prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ services:

redis:
container_name: immich_redis
image: docker.io/valkey/valkey:9@sha256:fb8d272e529ea567b9bf1302245796f21a2672b8368ca3fcb938ac334e613c8f
image: docker.io/valkey/valkey:9@sha256:546304417feac0874c3dd576e0952c6bb8f06bb4093ea0c9ca303c73cf458f63
healthcheck:
test: redis-cli ping || exit 1
restart: always
Expand All @@ -77,13 +77,15 @@ services:
- 5432:5432
shm_size: 128mb
restart: always
healthcheck:
disable: false

# set IMMICH_TELEMETRY_INCLUDE=all in .env to enable metrics
immich-prometheus:
container_name: immich_prometheus
ports:
- 9090:9090
image: prom/prometheus@sha256:d936808bdea528155c0154a922cd42fd75716b8bb7ba302641350f9f3eaeba09
image: prom/prometheus@sha256:2b6f734e372c1b4717008f7d0a0152316aedd4d13ae17ef1e3268dbfaf68041b
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
Expand All @@ -95,7 +97,7 @@ services:
command: ['./run.sh', '-disable-reporting']
ports:
- 3000:3000
image: grafana/grafana:12.3.0-ubuntu@sha256:cee936306135e1925ab21dffa16f8a411535d16ab086bef2309339a8e74d62df
image: grafana/grafana:12.3.1-ubuntu@sha256:d57f1365197aec34c4d80869d8ca45bb7787c7663904950dab214dfb40c1c2fd
volumes:
- grafana-data:/var/lib/grafana

Expand Down
4 changes: 3 additions & 1 deletion docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ services:

redis:
container_name: immich_redis
image: docker.io/valkey/valkey:9@sha256:fb8d272e529ea567b9bf1302245796f21a2672b8368ca3fcb938ac334e613c8f
image: docker.io/valkey/valkey:9@sha256:546304417feac0874c3dd576e0952c6bb8f06bb4093ea0c9ca303c73cf458f63
healthcheck:
test: redis-cli ping || exit 1
restart: always
Expand All @@ -69,6 +69,8 @@ services:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
shm_size: 128mb
restart: always
healthcheck:
disable: false

volumes:
model-cache:
16 changes: 16 additions & 0 deletions docs/docs/features/hardware-transcoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ For RKMPP to work:

5. (Optional) Enable hardware decoding for optimal performance.

<details>
<summary>immich.json</summary>

If you use a [configuration file](/install/config-file.md), use the `accel` option to select the hardware (e.g. `qsv` for Intel or `nvenc` for Nvidia). Set `accelDecode` to `true` if you want hardware decoding.

```json
{
"ffmpeg": {
"accel": "qsv",
"accelDecode": true
}
}
```

</details>

#### Single Compose File

Some platforms, including Unraid and Portainer, do not support multiple Compose files as of writing. As an alternative, you can "inline" the relevant contents of the [`hwaccel.transcoding.yml`][hw-file] file into the `immich-server` service directly.
Expand Down
36 changes: 36 additions & 0 deletions docs/docs/features/monitoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,40 @@ You can then make a new panel, specifying Prometheus as the data source for it.

-- TODO: add images and more details here

## Structured Logging

In addition to Prometheus metrics, Immich supports structured JSON logging which is ideal for log aggregation systems like Grafana Loki, ELK Stack, Datadog, Splunk, and others.

### Configuration

By default, Immich outputs human-readable console logs. To enable JSON logging, set the `IMMICH_LOG_FORMAT` environment variable:

```bash
IMMICH_LOG_FORMAT=json
```

:::tip
The default is `IMMICH_LOG_FORMAT=console` for human-readable logs with colors during development. For production deployments using log aggregation, use `IMMICH_LOG_FORMAT=json`.
:::

### JSON Log Format

When enabled, logs are output in structured JSON format:

```json
{"level":"log","pid":36,"timestamp":1766533331507,"message":"Initialized websocket server","context":"WebsocketRepository"}
{"level":"warn","pid":48,"timestamp":1766533331629,"message":"Unable to open /build/www/index.html, skipping SSR.","context":"ApiService"}
{"level":"error","pid":36,"timestamp":1766533331690,"message":"Failed to load plugin immich-core:","context":"Error"}
```

This format includes:

- `level`: Log level (log, warn, error, etc.)
- `pid`: Process ID
- `timestamp`: Unix timestamp in milliseconds
- `message`: Log message
- `context`: Service or component that generated the log

For more information on log formats, see [`IMMICH_LOG_FORMAT`](/install/environment-variables.md#general).

[prom-file]: https://github.com/immich-app/immich/releases/latest/download/prometheus.yml
1 change: 1 addition & 0 deletions docs/docs/install/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ These environment variables are used by the `docker-compose.yml` file and do **N
| `TZ` | Timezone | <sup>\*1</sup> | server | microservices |
| `IMMICH_ENV` | Environment (production, development) | `production` | server, machine learning | api, microservices |
| `IMMICH_LOG_LEVEL` | Log level (verbose, debug, log, warn, error) | `log` | server, machine learning | api, microservices |
| `IMMICH_LOG_FORMAT` | Log output format (`console`, `json`) | `console` | server | api, microservices |
| `IMMICH_MEDIA_LOCATION` | Media location inside the container ⚠️**You probably shouldn't set this**<sup>\*2</sup>⚠️ | `/data` | server | api, microservices |
| `IMMICH_CONFIG_FILE` | Path to config file | | server | api, microservices |
| `NO_COLOR` | Set to `true` to disable color-coded log output | `false` | server, machine learning | |
Expand Down
2 changes: 1 addition & 1 deletion e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@playwright/test": "^1.44.1",
"@socket.io/component-emitter": "^3.1.2",
"@types/luxon": "^3.4.2",
"@types/node": "^24.10.3",
"@types/node": "^24.10.4",
"@types/oidc-provider": "^9.0.0",
"@types/pg": "^8.15.1",
"@types/pngjs": "^6.0.4",
Expand Down
38 changes: 12 additions & 26 deletions e2e/src/api/specs/shared-link.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ describe('/shared-links', () => {
let user1: LoginResponseDto;
let user2: LoginResponseDto;
let album: AlbumResponseDto;
let metadataAlbum: AlbumResponseDto;
let deletedAlbum: AlbumResponseDto;
let linkWithDeletedAlbum: SharedLinkResponseDto;
let linkWithPassword: SharedLinkResponseDto;
Expand All @@ -41,18 +40,9 @@ describe('/shared-links', () => {

[asset1, asset2] = await Promise.all([utils.createAsset(user1.accessToken), utils.createAsset(user1.accessToken)]);

[album, deletedAlbum, metadataAlbum] = await Promise.all([
[album, deletedAlbum] = await Promise.all([
createAlbum({ createAlbumDto: { albumName: 'album' } }, { headers: asBearerAuth(user1.accessToken) }),
createAlbum({ createAlbumDto: { albumName: 'deleted album' } }, { headers: asBearerAuth(user2.accessToken) }),
createAlbum(
{
createAlbumDto: {
albumName: 'metadata album',
assetIds: [asset1.id],
},
},
{ headers: asBearerAuth(user1.accessToken) },
),
]);

[linkWithDeletedAlbum, linkWithAlbum, linkWithAssets, linkWithPassword, linkWithMetadata, linkWithoutMetadata] =
Expand All @@ -75,14 +65,14 @@ describe('/shared-links', () => {
password: 'foo',
}),
utils.createSharedLink(user1.accessToken, {
type: SharedLinkType.Album,
albumId: metadataAlbum.id,
type: SharedLinkType.Individual,
assetIds: [asset1.id],
showMetadata: true,
slug: 'metadata-album',
slug: 'metadata-slug',
}),
utils.createSharedLink(user1.accessToken, {
type: SharedLinkType.Album,
albumId: metadataAlbum.id,
type: SharedLinkType.Individual,
assetIds: [asset1.id],
showMetadata: false,
}),
]);
Expand All @@ -95,9 +85,7 @@ describe('/shared-links', () => {
const resp = await request(shareUrl).get(`/${linkWithMetadata.key}`);
expect(resp.status).toBe(200);
expect(resp.header['content-type']).toContain('text/html');
expect(resp.text).toContain(
`<meta name="description" content="${metadataAlbum.assets.length} shared photos &amp; videos" />`,
);
expect(resp.text).toContain(`<meta name="description" content="1 shared photos &amp; videos" />`);
});

it('should have correct asset count in meta tag for empty album', async () => {
Expand Down Expand Up @@ -144,9 +132,7 @@ describe('/shared-links', () => {
const resp = await request(baseUrl).get(`/s/${linkWithMetadata.slug}`);
expect(resp.status).toBe(200);
expect(resp.header['content-type']).toContain('text/html');
expect(resp.text).toContain(
`<meta name="description" content="${metadataAlbum.assets.length} shared photos &amp; videos" />`,
);
expect(resp.text).toContain(`<meta name="description" content="1 shared photos &amp; videos" />`);
});
});

Expand Down Expand Up @@ -271,20 +257,20 @@ describe('/shared-links', () => {
);
});

it('should return metadata for album shared link', async () => {
it('should return metadata for individual shared link', async () => {
const { status, body } = await request(app).get('/shared-links/me').query({ key: linkWithMetadata.key });

expect(status).toBe(200);
expect(body.assets).toHaveLength(0);
expect(body.album).toBeDefined();
expect(body.assets).toHaveLength(1);
expect(body.album).not.toBeDefined();
});

it('should not return metadata for album shared link without metadata', async () => {
const { status, body } = await request(app).get('/shared-links/me').query({ key: linkWithoutMetadata.key });

expect(status).toBe(200);
expect(body.assets).toHaveLength(1);
expect(body.album).toBeDefined();
expect(body.album).not.toBeDefined();

const asset = body.assets[0];
expect(asset).not.toHaveProperty('exifInfo');
Expand Down
2 changes: 1 addition & 1 deletion i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2153,7 +2153,7 @@
"trigger": "Trigger",
"trigger_asset_uploaded": "Asset Uploaded",
"trigger_asset_uploaded_description": "Triggered when a new asset is uploaded",
"trigger_description": "An event that kick off the workflow",
"trigger_description": "An event that kicks off the workflow",
"trigger_person_recognized": "Person Recognized",
"trigger_person_recognized_description": "Triggered when a person is detected",
"trigger_type": "Trigger type",
Expand Down
2 changes: 1 addition & 1 deletion mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ experimental_monorepo_root = true
[tools]
node = "24.11.1"
flutter = "3.35.7"
pnpm = "10.24.0"
pnpm = "10.27.0"
terragrunt = "0.93.10"
opentofu = "1.10.7"
java = "25.0.1"
Expand Down
2 changes: 1 addition & 1 deletion open-api/typescript-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"@oazapfts/runtime": "^1.0.2"
},
"devDependencies": {
"@types/node": "^24.10.3",
"@types/node": "^24.10.4",
"typescript": "^5.3.3"
},
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"version": "0.0.1",
"description": "Monorepo for Immich",
"private": true,
"packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a",
"packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a",
"engines": {
"pnpm": ">=10.0.0"
}
Expand Down
Loading
Loading