pictaria-server README

The project's README from github.com/pictaria-ai/pictaria-server. As one markdown file: README.md.

Pictaria Server

Self-hosted photo intelligence, enrichment, curation, and automation for your Immich library. Pictaria Server helps you explore and understand your collection, optionally enrich it with AI, review your best photos, and build Smart Albums that stay up to date—all through a private web dashboard running on your hardware.

Use it as a standalone Immich application or pair it with Pictaria Frame. When paired, it also powers centralized curation, remote control, voice features, ambient information, and display history across your Frames. It runs as one container with one Node process and zero npm dependencies.

Pictaria Server includes:

See Pictaria Server

These are the same privacy-scrubbed product views published on the Pictaria Server page. Select any image to open it at full size.

Pictaria Server Insights page with collection statistics and an expanded photo timeline
Insights — collection statistics, people and places, and the timeline behind them.
Pictaria Server Curate page with stacks of related photos and suggested keepers
Curate — related shots become stacks with a suggested keeper and a human decision.
Pictaria Server Smart Albums page with scheduled rule-based Immich albums
Smart Albums — saved rules keep real Immich albums synchronized on a schedule.
Pictaria Server Frame Metrics page with a display ledger and most-shown photos
Frame Metrics — a local display ledger shows what each Pictaria Frame has presented.

Quick start (Docker)

Production installs should select an explicit reviewed release. The source tag and matching versioned GHCR image keep the deployment inputs aligned.

PICTARIA_RELEASE=v1.1.0 # replace with the release you are installing
mkdir pictaria-server && cd pictaria-server
curl -fsSLO \
  "https://raw.githubusercontent.com/pictaria-ai/pictaria-server/${PICTARIA_RELEASE}/docker-compose.yml"
curl -fsSL -o .env \
  "https://raw.githubusercontent.com/pictaria-ai/pictaria-server/${PICTARIA_RELEASE}/.env.example"
chmod 600 .env
# edit .env: set IMMICH_BASE_URL, IMMICH_API_KEY, and a non-empty APP_PASSWORD
# for off-machine Docker backups, also follow docs/BACKUP.md to mount the
# destination and set BACKUP_DIR to its container path
docker compose config --images # must name the selected numeric release, not latest
docker compose pull
docker compose up -d

Open http://your-host:4080, enter your app password, and you're in.

Pictaria Server v1 requires Immich 2.0 or newer. It has been tested with Immich 2.7.5 and 3.1.0; other versions may work but are not explicitly validated. See Immich compatibility before upgrading Immich.

Quick start (bare Node)

Requires Node 22.16+ on the 22 line, or 23.8+ beyond it (node:sqlite and its online-backup API; earlier builds fail at boot — engines says ^22.16.0 || >=23.8.0). The production container and CI suite use Node 22; newer supported versions work as well.

PICTARIA_RELEASE=v1.1.0 # replace with the release you are installing
git clone --branch "$PICTARIA_RELEASE" --depth 1 \
  https://github.com/pictaria-ai/pictaria-server.git
cd pictaria-server
cp .env.example .env   # edit as above
chmod 600 .env
npm start

Development preview

To evaluate unreleased work, clone the moving main branch and build it locally. This path can change between releases and is not the production install or upgrade procedure:

git clone https://github.com/pictaria-ai/pictaria-server.git
cd pictaria-server
cp .env.example .env   # edit as above
chmod 600 .env
docker compose up -d --build

For every later production update, follow Upgrading and select the release you intend to run.

Either way, continue with the first-run checklist — connect Immich, install the app, and switch on the optional features one by one.

Configuration

Only a handful of values have to be environment variables: the listen address/port, the data paths, and APP_PASSWORD. Everything else — the Immich connection, AI providers and models, voice, weather, backups — can be set (and changed at any time, no restart) from the web UI. Provider connections and model identifiers live under Settings → AI Providers; choose the provider used for new enrichment runs on Enrich, where the selection is remembered across visits and restarts. Every UI-editable field also has an env-var form for infrastructure-as-code setups; a value saved in the UI overrides the environment until cleared.

See docs/CONFIGURATION.md for the complete reference and .env.example for an annotated starter file. The short version:

Variable Required What it does
IMMICH_BASE_URL yes (env or Settings) Server-reachable Immich address over HTTP or HTTPS, e.g. http://immich.local:2283 or https://photos.example.com; omit trailing /api
IMMICH_API_KEY yes (env or Settings) An Immich API key for your account — full access not required; see the least-privilege checklist in Getting started
APP_PASSWORD strongly recommended Protects the API and web UI (env-only). Blank or missing refuses startup unless ALLOW_INSECURE_OPEN=true.
ALLOW_INSECURE_OPEN no Dangerous explicit opt-in to run without authentication. Anyone who can reach the server can browse photos and perform Immich-backed mutations.
OPENAI_API_KEY / OPENAI_COMPATIBLE_* / LMSTUDIO_* / OPENROUTER_* / OLLAMA_* / VENICE_* for Enrich Provider connections and vision-model identifiers; configure the same values under Settings → AI Providers
DEFAULT_PROVIDER no Initial/infrastructure fallback for Enrich; choosing a provider on the Enrich page saves the active choice for future runs
INFERENCE_HOST_LABEL no Optional operator-authored label saved with new Enrich run summaries for comparing inference machines; editable under Settings → Enrich
TTS_PROVIDER + keys for Voice openai or elevenlabs text-to-speech
WEATHER_DEFAULT_LOCATION for Ambient Default weather place (any city name or US zip)
GEOCODING_PROVIDER + GEOAPIFY_API_KEY optional Reverse geocoding for nicer location labels

Both HTTP and HTTPS Immich connections are supported. Publicly trusted HTTPS certificates work normally; a private/home CA must be trusted by the Node process inside the container. Addresses without a scheme default to HTTP, so enter the full https:// address when you intend to use TLS. See Configuration for a private-CA example and the distinction between the server-reachable IMMICH_BASE_URL and browser-facing IMMICH_PUBLIC_URL.

All state lives in one directory (./data, or the /data volume in Docker): enrichment.sqlite, smart-albums.json, frame.db, settings.json, insights.sqlite, custom models under wake-word-models/, and automatic snapshots under backups/.

Privacy

Pictaria Server has no telemetry, analytics, or Pictaria-operated cloud service. It does make the connections needed for features you choose:

Insights statistics and app metrics are stored locally. Voice usage counters record only a command label and timestamp; transcripts are never stored.

Exposing beyond your LAN

Pictaria Server is built as a LAN tool: it speaks plain HTTP and uses a single shared password. If you want to reach it from outside your network, don't port-forward it directly — put it behind an HTTPS reverse proxy (Caddy, nginx, Traefik) or a VPN/tunnel like Tailscale or WireGuard, and keep the server itself listening only where the proxy can reach it.

If Docker publishes the port directly on a Tailscale or other VPN-owned host address, read the VPN boot-order guidance. The default 4080:4080 mapping does not have that address dependency.

When it is only ever reached through HTTPS, also set SESSION_COOKIE_SECURE=true so the browser session cookie is never sent over plain HTTP (leave it unset for normal LAN use — it breaks plain-http:// login). For a public custom domain, set BROWSER_ALLOWED_HOSTS to its exact host or host:port (comma-separated for more than one). IP addresses, single-label LAN names, .local, .home.arpa, and Tailscale .ts.net names are accepted automatically. This allowlist keeps an arbitrary DNS-rebinding hostname from serving the login UI, becoming its own cookie/Origin trust anchor, or reaching the API in deliberate open mode.

Configure the proxy to preserve the browser-facing Host header: Pictaria compares that host and port with the browser's Origin and rejects foreign browser Fetch Metadata to prevent cross-origin API requests, including in deliberate open mode. Native and programmatic clients that send no browser headers remain supported. TLS may terminate at the proxy; the schemes do not need to match. nginx does not preserve the public host by default, so its Pictaria location needs this explicit directive:

proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

Whatever proxy you use, confirm that the upstream Host remains the public host and port the browser requested and that the same authority is in BROWSER_ALLOWED_HOSTS. To keep one device's failed logins from locking out every user behind the proxy, set TRUSTED_PROXY_IPS to the proxy's exact address or narrow network (comma-separated IPs/CIDRs). Pictaria ignores X-Forwarded-For unless the direct peer is trusted, and evaluates trusted chains from right to left; never configure a catch-all such as 0.0.0.0/0. Leave this unset for direct LAN/VPN access. The auth model stays a single shared password with bounded concurrent password work, a ~1s failure delay, per-client lockout, and a higher global failure ceiling; there are no per-user accounts or roles, so treat access as all-or-nothing when deciding who gets the URL.

API

Everything is under /api/ and requires auth when APP_PASSWORD is set: programmatic callers send the password as an X-App-Password header or Authorization: Bearer; the web UI logs in once (POST /api/session) and rides an HttpOnly session cookie for 30 days — the browser never stores the password itself. Login bodies are limited to 4 KiB and must arrive within 5 seconds. Cookie-authenticated and deliberate-open browser API requests reject foreign/null origins and cross-/same-site Fetch Metadata; native clients without browser headers remain supported. JSON endpoints also require an application/json content type. Session signatures combine the current password with a random per-installation secret stored in the data directory, so they survive ordinary restarts, immediately expire when the password changes, and cannot be used to test password guesses by themselves. Upgrading an older install to this session format asks each browser to log in once. /api/health always answers, but tells unauthenticated callers only { ok, service, time, authRequired } — configuration details need credentials. Feature prefixes: /api/review, /api/enrich, /api/taxonomy, /api/albums, /api/frame (including wake-word models), /api/voice, /api/photos, /api/assets, /api/weather, /api/insights, /api/activity, /api/settings, /api/support, /api/backup.

Text-to-speech uses authenticated POST /api/voice/tts with Content-Type: application/json and a body shaped as { "text": "What to say" }. A successful response is the provider's audio payload; the X-TTS-Provider and optional X-TTS-Voice response headers describe it. GET /api/voice/tts is not supported and never dispatches provider work.

Documentation

Development

npm test   # node --test; no dependencies to install

The suite includes browser-level smoke tests (test/browser/) that drive the real admin pages — login gate, Insights lens search, Curate, Smart Album create/validate — in headless Chrome over raw CDP, still with zero npm dependencies. They use a system Chrome/Chromium if one is installed (set PICTARIA_CHROME_BIN for a non-standard path) and skip cleanly when none is found.

Design rules

License

Pictaria Server is free software, licensed under the GNU Affero General Public License, version 3 only (AGPL-3.0-only). You may run the unmodified server freely, and private modifications used only by you do not need to be published. Commercial use, including paid hosting, is permitted. If you distribute a modified copy or let others interact with a modified version over a network, you must offer those users the corresponding source under the same license.

Contributions are welcome under the same license with a lightweight Developer Certificate of Origin sign-off — no CLA.

Copyright © 2026 Pictaria and contributors.

The files under public/brand/ are included under the AGPL for copyright purposes. The license does no

pictaria-server (pictaria-ai/pictaria-server)

Searching Database…

What is this?Pictaria Server is a self-hosted companion for Immich that adds photo insights, AI-based enrichment, human-reviewed curation, and auto-syncing Smart Albums, all managed through a private web dashboard running on your own hardware.
Do I need an AI provider to use the enrich features?AI classification is optional and works with a locally hosted model via Ollama, LM Studio, or llama.cpp, or with a cloud provider such as OpenAI, OpenRouter, or Venice through your own API key.
How do I get it running?The recommended path is Docker: run the versioned GHCR image with your Immich address and API key as environment variables and open the web UI. A bare Node setup is also possible if you build it yourself, but it requires Node 22.16+ (or 23.8+).
Do I need an existing Immich server to use this?Yes, Pictaria Server is not a standalone photo library; it connects to an existing Immich installation and requires version 2.0 or newer, tested with 2.7.5 and 3.1.0.
Does it support multiple user accounts?No, access is controlled by one shared APP_PASSWORD with no per-user accounts or roles, so anyone who has the URL and password gets full access.
What data does it send outside my network?Only what you enable: the enrichment image goes to your chosen AI provider (local or cloud), weather lookups go to Open-Meteo, optional place naming goes to Geoapify, and there is no Pictaria-run telemetry or cloud service.
What is Pictaria Frame?Pictaria Frame is a companion digital photo frame device. When paired with Pictaria Server it gains centralized curation, remote control, voice features, and ambient display information.
What are Smart Albums exactly?Smart Albums are saved Immich searches (by people, tags, places, cameras, or ranked free-text) that Pictaria keeps synced to a real Immich album on a schedule, with an optional Best of mode that re-ranks results using your own curation scores.
How does the Curate review step decide the best photo?Same-moment photos collapse into Stacks with a suggested keeper: a silver star from automatic frame-worthiness scoring, or a gold star when an optional AI referee has compared the group side by side.
Can I safely expose it to the internet, not just my LAN?It's built as a LAN tool over plain HTTP with one shared password, so to reach it remotely you should put it behind an HTTPS reverse proxy or a VPN/tunnel like Tailscale or WireGuard instead of port-forwarding it directly.
Size
6.6 MB
Stars
192
Age
1mo
Commits · 90 Days