Files
immich-folio/README.md

7.9 KiB

Immich Folio

A self-hosted photography portfolio powered by Immich. Turns your Immich albums into a beautiful, public-facing gallery — without ever exposing your Immich server to the internet.

Immich Folio acts as a secure reverse proxy between your visitors and your private Immich instance. Your Immich server stays on your local network, completely invisible to the outside world.

Homepage — Editorial theme Photo grid — Editorial theme

Features

  • Configurable hero layouts — split, fullbleed, minimal, stacked (image + thumbnail strip), typographic (text-only), or mosaic (multi-image grid)
  • Hero image carousel — single image or crossfade carousel of multiple Immich assets
  • Masonry photo grid — responsive layout with natural aspect ratios and configurable columns, gap, and aspect ratio
  • Uniform grid mode — switch to a fixed-aspect uniform grid per-subpage or globally
  • Showcase / filmstrip / editorial-flow layouts — featured hero + grid, horizontal scroll strips, or alternating full-width and paired images
  • Per-subpage grid overrides — each subpage can define its own columns, gap, aspect ratio, and layout mode
  • Fullscreen lightbox — keyboard and swipe navigation, EXIF panel, adjacent image preloading
  • EXIF metadata on hover — camera body, lens, focal length, aperture, shutter speed, ISO shown directly on the grid
  • ThumbHash placeholders — instant blurred previews while full images load

Content & Organization

  • Subpage grouping — organize albums into named collections (e.g. /japan/tokyo-2023)
  • Auto-generated slugs — URL slugs derived from album names automatically
  • YAML gallery config — all gallery structure defined in a single content/gallery.yaml file
  • Markdown about page — content/about.md with frontmatter for portrait, name, location, and gear list
  • Dynamic OG images — auto-generated social preview images per album

Security

Concern Protection
Server exposure Immich URL and port are never sent to the browser — all requests are proxied server-side
API key Stored only in .env.local, never included in client-side code or responses
Asset IDs Immich UUIDs are encrypted (AES-256) into opaque tokens — visitors cannot guess or enumerate asset IDs
Album scope Only albums explicitly listed in gallery.yaml are accessible — everything else is blocked
Password protection Individual subpages can require a password before content is revealed
Rate limiting Per-IP sliding-window rate limiter on the image proxy (configurable RPM)
No direct access Visitors interact only with the Next.js app; they have zero knowledge of your Immich server's existence

Production Ready: The codebase has been fully audited for production. It uses strict Content Security Policy (No unsafe-eval), strong cryptographic patterns (SHA-256 for IV generation instead of MD5), lengths-capped inputs for OG dynamic generation to prevent DoS, and is completely free of N+1 database queries since the API isolates Immich.

Infrastructure

  • Health check endpoint — GET /api/health for uptime monitoring and container orchestration
  • In-memory caching — configurable TTL for Immich API responses
  • Standalone Docker image — multi-stage build, runs as non-root, ~150 MB

Quick Start

# Clone and install
git clone https://github.com/ralksta/immich-folio.git
cd immich-folio
npm install

# Configure
cp .env.local.example .env.local
# Edit .env.local with your Immich server URL and API key

cp content/gallery.yaml.example content/gallery.yaml
# Edit gallery.yaml with your album UUIDs

# Run
npm run dev

Configuration

Environment Variables (.env.local)

# Required
IMMICH_API_URL=http://your-immich-server:2283
IMMICH_API_KEY=your-api-key

# Optional
SITE_TITLE=My Photography            # default: "Gallery"
SITE_SUBTITLE=A visual journal        # default: empty
CACHE_TTL=300                          # seconds, default: 300
RATE_LIMIT_RPM=120                     # requests/min/IP, default: 120

All gallery structure — hero images, albums, subpages, grid layout, footer — is defined in content/gallery.yaml.

→ Gallery Configuration Guide

Theming

Seven built-in presets with distinct visual identities — or mix and match with fine-grained control over colors, fonts, corners, photo frames, hero layout, and grid style.

theme: studio # or: minimal, editorial, classic, noir, monograph, botanica
Studio
Leica-inspired, red dot
Minimal
Swiss brutalist, fullbleed hero
Studio theme Minimal theme
Editorial
Cinematic magazine, warm charcoal
Classic
Gilded gallery, gold accents
Editorial theme Classic theme
Noir
Darkroom analog, film noir
Monograph
Typographic, book-like
Noir theme Monograph theme
Botanica
Organic, nature-inspired
Botanica theme

→ Theming Guide

Docker

services:
  lightbox:
    build: .
    container_name: immich-folio
    restart: unless-stopped
    ports:
      - '7211:7211'
    env_file:
      - .env.local
    volumes:
      - ./content:/app/content:ro

Run with:

docker compose up -d

The gallery will be available at http://localhost:7211.

Standalone Docker

# Build
docker build -t immich-folio .

# Run
docker run -d \
  --name immich-folio \
  --restart unless-stopped \
  -p 7211:7211 \
  --env-file .env.local \
  -v ./content:/app/content:ro \
  immich-folio

Note: The content/ volume mount lets you update gallery.yaml and about.md without rebuilding the image.

Health Check

The container includes a built-in health check at /api/health:

curl http://localhost:7211/api/health

Reverse Proxy

Put Immich Folio behind nginx / Caddy / Traefik with TLS. Example Caddy config:

photos.example.com {
    reverse_proxy localhost:7211
}

Tech Stack

  • Next.js 16 (App Router, standalone output)
  • React 19
  • TypeScript
  • Vanilla CSS (no framework)

License

Private — all rights reserved.