# Lumina

Lumina is a self-hosted photo and video library for organizing, browsing, and sharing personal media. It runs on your own hardware, stores files in a standard directory structure, and provides a responsive web interface for desktop and mobile devices.

## Features

- Automatic photo and video indexing
- Timeline, album, favorite, and archive views
- Metadata extraction for dates, cameras, and locations
- Search by filename, date, album, and metadata
- Duplicate detection using file hashes
- Thumbnail and preview generation
- Optional face and object recognition
- Shared albums with expiring links
- Multiple user accounts with role-based access
- Original-quality downloads
- Dark mode and mobile-friendly interface
- Docker-based deployment
- Read-only import mode for existing libraries

## Installation

### Requirements

- Docker Engine 24 or newer
- Docker Compose v2
- At least 2 GB of RAM
- A persistent directory for application data
- A directory containing your photos and videos

### Docker Compose

Create a `compose.yml` file:

```yaml
services:
  lumina:
    image: ghcr.io/example/lumina:latest
    container_name: lumina
    restart: unless-stopped
    ports:
      - "8080:8080"
    environment:
      LUMINA_DATABASE_URL: postgres://lumina:change-me@database:5432/lumina
      LUMINA_LIBRARY_PATH: /library
      LUMINA_DATA_PATH: /data
      LUMINA_SECRET_KEY: replace-with-a-long-random-value
    volumes:
      - ./photos:/library
      - ./data:/data
    depends_on:
      database:
        condition: service_healthy

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

Replace the example passwords and secret key, then start the services:

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

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

To update Lumina:

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

## Usage

### Importing media

Copy photos and videos into the mounted library directory:

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

Lumina periodically scans the library for changes. To start a scan immediately:

```bash
docker compose exec lumina lumina scan
```

Files are indexed in place by default. Lumina does not rename, move, or delete originals unless managed-library mode is enabled.

### Creating albums

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

Removing an item from an album does not remove the original file.

### Sharing an album

Open an album and select **Share**. Shared links can be protected with a password and configured with an expiration date. Administrators can review and revoke active links from **Settings → Sharing**.

### Backups

Back up all persistent directories used by the deployment:

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

Stop the application before taking a filesystem-level database backup, or use `pg_dump` while it is running:

```bash
docker compose exec -T database \
  pg_dump -U lumina lumina > lumina-backup.sql
```

The thumbnail cache in `./data/cache` may be excluded because Lumina can regenerate it.

## Configuration

Lumina is configured with environment variables. Changes require a container restart:

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

| Variable | Default | Description |
| --- | --- | --- |
| `LUMINA_DATABASE_URL` | Required | PostgreSQL connection URL |
| `LUMINA_LIBRARY_PATH` | `/library` | Path containing original media |
| `LUMINA_DATA_PATH` | `/data` | Path for thumbnails, indexes, and application data |
| `LUMINA_SECRET_KEY` | Required | Secret used to sign sessions and shared links |
| `LUMINA_BIND_ADDRESS` | `0.0.0.0` | Address on which the HTTP server listens |
| `LUMINA_PORT` | `8080` | HTTP server port |
| `LUMINA_BASE_URL` | Empty | Public URL, such as `https://photos.example.com` |
| `LUMINA_SCAN_INTERVAL` | `15m` | Interval between automatic library scans |
| `LUMINA_LIBRARY_MODE` | `readonly` | Either `readonly` or `managed` |
| `LUMINA_THUMBNAIL_QUALITY` | `82` | JPEG thumbnail quality from 1 to 100 |
| `LUMINA_TIMEZONE` | `UTC` | Time zone used for dates and scheduled tasks |
| `LUMINA_LOG_LEVEL` | `info` | Logging level: `debug`, `info`, `warn`, or `error` |
| `LUMINA_REGISTRATION_ENABLED` | `false` | Allows visitors to create accounts |
| `LUMINA_ML_ENABLED` | `false` | Enables face and object recognition |
| `LUMINA_TRUST_PROXY` | `false` | Trusts forwarded headers from a reverse proxy |

Boolean values accept `true` or `false`. Durations use values such as `30s`, `15m`, or `24h`.

### Reverse proxy

When Lumina is exposed through a reverse proxy, set its public URL and enable forwarded-header support:

```yaml
environment:
  LUMINA_BASE_URL: https://photos.example.com
  LUMINA_TRUST_PROXY: "true"
```

Terminate TLS at the proxy and forward requests to port `8080`. The proxy should preserve the `Host`, `X-Forwarded-For`, and `X-Forwarded-Proto` headers.

### Read-only libraries

For an existing photo collection, mount the library as read-only and keep `LUMINA_LIBRARY_MODE` set to `readonly`:

```yaml
volumes:
  - /mnt/photos:/library:ro
  - ./data:/data
```

Uploads, edits to original files, and deletion are disabled in this mode. Albums, favorites, labels, and other Lumina metadata remain writable in the database.