# LumaShelf

LumaShelf is a self-hosted photo and video library for organizing, browsing, and sharing personal media. It runs on your own hardware, stores original files without modifying them, and provides a responsive web interface for desktop and mobile devices.

## Features

- Automatic photo and video indexing
- Timeline, album, folder, and map views
- Search by filename, date, camera, location, and tags
- EXIF and video metadata extraction
- Duplicate detection using content hashes
- Background thumbnail and preview generation
- Private and public share links
- Multiple user accounts with configurable permissions
- Favorites, ratings, captions, and custom tags
- Optional face recognition and object classification
- Read-only library mode for existing photo archives
- Docker-based deployment
- PostgreSQL database support
- Dark and light themes
- REST API for integrations and automation

## Installation

### Requirements

- Docker 24 or newer
- Docker Compose v2
- PostgreSQL 15 or newer
- At least 2 GB of memory
- A mounted directory containing your photo library

Hardware-accelerated video transcoding and machine-learning features are optional.

### Docker Compose

Create a working directory:

```bash
mkdir lumashelf
cd lumashelf
```

Create a `compose.yaml` file:

```yaml
services:
  app:
    image: ghcr.io/lumashelf/lumashelf:latest
    container_name: lumashelf
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      LUMASHELF_DATABASE_URL: postgres://lumashelf:change-me@database:5432/lumashelf
      LUMASHELF_LIBRARY_PATH: /library
      LUMASHELF_DATA_PATH: /data
      LUMASHELF_SECRET_KEY: replace-with-a-long-random-value
    volumes:
      - ./data:/data
      - /path/to/your/photos:/library:ro
    depends_on:
      database:
        condition: service_healthy

  database:
    image: postgres:16-alpine
    container_name: lumashelf-database
    restart: unless-stopped
    environment:
      POSTGRES_DB: lumashelf
      POSTGRES_USER: lumashelf
      POSTGRES_PASSWORD: change-me
    volumes:
      - ./postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U lumashelf"]
      interval: 10s
      timeout: 5s
      retries: 5
```

Replace `/path/to/your/photos`, the database password, and the secret key before starting the application.

Start the services:

```bash
docker compose up -d
```

Open `http://localhost:8080` and follow the setup wizard to create the administrator account.

### Updating

Pull the latest image and recreate the application container:

```bash
docker compose pull
docker compose up -d
```

Database migrations run automatically when the application starts. Back up the database and `data` directory before upgrading.

## Usage

### Importing a library

LumaShelf scans the directory configured by `LUMASHELF_LIBRARY_PATH`. The library is mounted read-only in the example configuration, so originals cannot be renamed or deleted through the application.

To start a scan:

1. Sign in as an administrator.
2. Open **Settings → Libraries**.
3. Select the library.
4. Choose **Scan now**.

New and changed files are indexed in the background. Progress and errors appear under **Administration → Jobs**.

Supported formats include JPEG, PNG, WebP, GIF, HEIC, TIFF, RAW files with embedded previews, MP4, MOV, and WebM. Format availability may depend on the codecs installed in the container image.

### Creating albums

Open the timeline, select one or more items, and choose **Add to album**. Albums reference files in the library and do not create additional copies.

### Sharing photos

Open an album or selection and choose **Share**. Share links can include:

- An expiration date
- A password
- Download permissions
- Original-file access
- A custom title and description

Anyone with an active link can access the shared content, so use passwords for sensitive albums.

### Uploading files

Uploads are disabled when the library is configured as read-only. To enable uploads, mount the library with write access and set:

```yaml
environment:
  LUMASHELF_UPLOADS_ENABLED: "true"
volumes:
  - /path/to/your/photos:/library
```

Uploaded files are stored in date-based folders by default:

```text
/library/2026/07/30/
```

The folder template can be changed with `LUMASHELF_UPLOAD_PATH_TEMPLATE`.

## Configuration

LumaShelf is configured with environment variables.

| Variable | Default | Description |
| --- | --- | --- |
| `LUMASHELF_DATABASE_URL` | Required | PostgreSQL connection URL |
| `LUMASHELF_SECRET_KEY` | Required | Secret used to sign sessions and share tokens |
| `LUMASHELF_LIBRARY_PATH` | `/library` | Path containing original media |
| `LUMASHELF_DATA_PATH` | `/data` | Path for thumbnails, previews, and application state |
| `LUMASHELF_BIND` | `0.0.0.0:8080` | HTTP listen address |
| `LUMASHELF_BASE_URL` | Empty | Public URL, such as `https://photos.example.com` |
| `LUMASHELF_LOG_LEVEL` | `info` | Logging level: `debug`, `info`, `warn`, or `error` |
| `LUMASHELF_UPLOADS_ENABLED` | `false` | Allow users with permission to upload files |
| `LUMASHELF_UPLOAD_PATH_TEMPLATE` | `{year}/{month}/{day}` | Folder pattern for uploaded files |
| `LUMASHELF_SCAN_INTERVAL` | `6h` | Interval between automatic library scans; use `0` to disable |
| `LUMASHELF_WORKERS` | `2` | Number of background processing workers |
| `LUMASHELF_THUMBNAIL_FORMAT` | `webp` | Generated thumbnail format |
| `LUMASHELF_THUMBNAIL_QUALITY` | `82` | Thumbnail quality from 1 to 100 |
| `LUMASHELF_TRANSCODING_ENABLED` | `true` | Generate browser-compatible video previews |
| `LUMASHELF_ML_ENABLED` | `false` | Enable face and object classification |
| `LUMASHELF_TRUST_PROXY` | `false` | Trust forwarded headers from a reverse proxy |

Generate a suitable secret key with:

```bash
openssl rand -hex 32
```

### Reverse proxy

When running behind a reverse proxy, set the public URL and enable trusted proxy headers:

```yaml
environment:
  LUMASHELF_BASE_URL: https://photos.example.com
  LUMASHELF_TRUST_PROXY: "true"
```

The proxy must forward `Host`, `X-Forwarded-For`, and `X-Forwarded-Proto`. TLS should be terminated at the proxy.

### Machine-learning features

Face recognition and object classification require additional memory and processing time. Enable them with:

```yaml
environment:
  LUMASHELF_ML_ENABLED: "true"
```

Existing items are not processed automatically after this setting changes. Start a metadata reprocessing job from **Administration → Jobs**.

### Backups

A complete backup includes:

- The PostgreSQL database
- The directory configured by `LUMASHELF_DATA_PATH`
- The original photo library

Example database backup:

```bash
docker compose exec -T database pg_dump -U lumashelf lumashelf > lumashelf.sql
```

Thumbnails and previews can be regenerated, but backing up the data directory reduces recovery time. Always verify backups by restoring them in a separate environment.