# LumaVault

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

## Features

- Automatic photo and video indexing
- Timeline, album, folder, and map views
- Full-resolution original file preservation
- Thumbnail and video preview generation
- Search by filename, date, camera, location, and tags
- EXIF and GPS metadata extraction
- Duplicate detection using content hashes
- Private and password-protected shared albums
- Multiple user accounts with separate libraries
- Background imports from watched directories
- Optional machine-learning features for face and object recognition
- Docker-based deployment
- PostgreSQL database support
- Dark mode and installable PWA

## Requirements

- Docker Engine 24 or later
- Docker Compose v2
- PostgreSQL 15 or later
- At least 2 GB of RAM
- A writable directory for originals, previews, and application data

For large libraries or machine-learning features, 8 GB of RAM and SSD-backed storage are recommended.

## Installation

Create a project directory:

```bash
mkdir lumavault
cd lumavault
```

Create a `compose.yaml` file:

```yaml
services:
  app:
    image: ghcr.io/example/lumavault:latest
    container_name: lumavault
    restart: unless-stopped
    depends_on:
      database:
        condition: service_healthy
    ports:
      - "8080:8080"
    environment:
      DATABASE_URL: postgres://lumavault:change-me@database:5432/lumavault
      APP_SECRET: replace-with-a-long-random-value
      TZ: America/Toronto
    volumes:
      - ./photos:/library
      - ./data:/var/lib/lumavault

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

Start the application:

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

Open `http://localhost:8080` and create the first administrator account.

Before exposing LumaVault to the internet, change the example database password and application secret. Use a reverse proxy with HTTPS for public deployments.

## Usage

### Importing media

Copy photos and videos into the mounted library directory:

```bash
cp -R ~/Pictures/. ./photos/
```

LumaVault periodically scans the directory for new files. To start a scan immediately:

```bash
docker compose exec app lumavault scan
```

Files under `/library` are treated as originals. LumaVault stores generated thumbnails, indexes, and other derived data under `/var/lib/lumavault`.

### Creating albums

1. Open **Library**.
2. Select one or more items.
3. Choose **Add to album**.
4. Create a new album or select an existing one.

Albums reference the original files and do not create additional copies.

### Sharing an album

Open an album and select **Share**. You can set an expiration date, require a password, and allow or disable downloads. Anyone with the generated link can view the shared album according to those settings.

### Backing up

Back up all three persistent directories:

```text
photos/
data/
postgres/
```

Stop the services before taking a filesystem-level backup to ensure database consistency:

```bash
docker compose down
```

After the backup completes, restart the application:

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

## Configuration

LumaVault is configured with environment variables.

| Variable | Default | Description |
| --- | --- | --- |
| `DATABASE_URL` | Required | PostgreSQL connection URL |
| `APP_SECRET` | Required | Secret used to sign sessions and shared links |
| `TZ` | `UTC` | Time zone used for dates and scheduled tasks |
| `HTTP_PORT` | `8080` | Internal HTTP server port |
| `LIBRARY_PATH` | `/library` | Directory containing original media |
| `DATA_PATH` | `/var/lib/lumavault` | Directory for generated and application data |
| `SCAN_INTERVAL` | `15m` | Interval between automatic library scans |
| `THUMBNAIL_FORMAT` | `webp` | Generated thumbnail format |
| `THUMBNAIL_QUALITY` | `82` | Thumbnail quality from 1 to 100 |
| `VIDEO_TRANSCODING` | `true` | Generate browser-compatible video previews |
| `ML_ENABLED` | `false` | Enable face and object recognition |
| `PUBLIC_URL` | Empty | External URL used when generating share links |
| `TRUST_PROXY` | `false` | Trust forwarded headers from a reverse proxy |
| `LOG_LEVEL` | `info` | Logging level: `debug`, `info`, `warn`, or `error` |

Example configuration:

```yaml
environment:
  DATABASE_URL: postgres://lumavault:strong-password@database:5432/lumavault
  APP_SECRET: 7f3b9c1d-replace-this-with-a-random-secret
  TZ: Europe/Berlin
  PUBLIC_URL: https://photos.example.com
  TRUST_PROXY: "true"
  SCAN_INTERVAL: 30m
  ML_ENABLED: "true"
```

Restart the application after changing configuration:

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

## Updating

Pull the latest image and recreate the containers:

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

Database migrations run automatically during startup. Back up your library and database before major-version upgrades.

## Supported Formats

Commonly supported formats include:

- Images: JPEG, PNG, WebP, GIF, TIFF, HEIC, and major RAW formats
- Video: MP4, MOV, WebM, MKV, and AVI

Actual decoding support depends on the codecs included in the container image.

## License

LumaVault is licensed under the AGPL-3.0 License. See `LICENSE` for details.