A PhotoPrism® Portal stores a single branding theme and distributes it to every instance in the cluster. A theme bundles the colors, login-page wallpaper, logo, favicon, and sign-in button icons, so a branding change made once on the Portal propagates to all instances automatically.

Theme assets are served under the /_theme/ URL path on each node. A theme is identified by an app.js entry file and an optional version.txt; the Portal never distributes a theme that has no app.js.

Theme Directories

Themes live under the configuration directory (storage/config/ by default). Three locations matter, depending on the node role:

Directory Role Purpose
storage/config/theme/ any The active theme directory served at /_theme/. Overridable with PHOTOPRISM_THEME_PATH.
storage/config/portal/theme/ Portal The Portal’s canonical cluster theme — the one it distributes. Used when it contains app.js.
storage/config/node/theme/ instance Where an instance stores the Portal-provided theme after downloading it.

Resolution rules:

  • On the Portal, the distributed theme is taken from portal/theme/ when that directory exists and contains a non-empty app.js; otherwise the Portal falls back to its own theme/ directory.
  • On an instance, when node/theme/ holds a valid theme (a non-empty app.js, so a version can be detected), the instance switches its active theme path to node/theme/. The cluster theme therefore takes effect without touching the instance’s own theme/ directory.

Two marker files drive the workflow: app.js must be present and non-empty for a theme to be considered valid and distributable, and version.txt carries the version string the Portal and instances compare to decide when a refresh is needed.

What a Theme Contains

Theme files are served under /_theme/ and referenced by a few configuration options. PhotoPrism resolves each asset from the active theme directory when the named file is present:

Asset Option / detection Notes
Theme entry app.js Required marker; carries custom CSS and color tokens. An optional version.txt sits beside it.
Login wallpaper auto-detected The first *.webp, then *.avif, then *.jpg file in the theme directory becomes the login background.
App icon / logo PHOTOPRISM_APP_ICON When the named file exists in the theme directory, it is served as the app logo / PWA icon.
Favicon PHOTOPRISM_SITE_FAVICON Resolved from the theme directory when present, otherwise the built-in favicon.
Share preview PHOTOPRISM_SITE_PREVIEW Social/sharing preview image, served from the theme directory.
Sign-in button PHOTOPRISM_OIDC_ICON Icon shown on the “Continue with <provider>” button, served from the theme directory.

Downloadable theme archives are validated for safety: only an allowlist of file types is accepted, archive size and entry counts are capped, path traversal is rejected, and private-network download sources are disallowed by default.

Login Page & Logo Customization

To rebrand the login page and logos across the cluster, place the assets in the Portal’s theme directory and let provisioning distribute them:

  • Login background: drop a *.webp (preferred), *.avif, or *.jpg image into the theme directory — the first match becomes the login wallpaper on every instance.
  • Logo / app icon: add the logo file and point PHOTOPRISM_APP_ICON at it.
  • Favicon and share preview: set PHOTOPRISM_SITE_FAVICON and PHOTOPRISM_SITE_PREVIEW to files in the theme directory.
  • Colors and CSS: ship them in app.js, so contrast and brand colors can be tuned without rebuilding the application image.

For replacing larger static assets (for example a fully custom login or registration page), a node can also serve files from a storage/web overlay directory — see Web Overlay. The theme mechanism is the cluster-distributed path; the web overlay is node-local static content.

How Provisioning Works

The Portal is the source of truth for the cluster theme. Distribution happens in four steps:

  1. Seed the Portal theme. On startup, if PHOTOPRISM_THEME_URL is set and the Portal’s theme directory is empty, the Portal downloads the archive and installs it. If the directory already contains files, the auto-install is skipped — a manually installed or customized theme is never overwritten.
  2. Portal serves the theme. The Portal exposes the theme as a zip at GET /api/v1/cluster/theme, built from its canonical theme directory (requires a non-empty app.js). Requests are allowed from the cluster network range or with an authenticated cluster download permission.
  3. Instances download and refresh. When an instance registers or boots, it compares the Portal-advertised theme version with the version installed in its node/theme/ directory:
    • No local app.js → download.
    • Local version differs from the Portal version → download and overwrite node/theme/.
    • Versions match → keep the installed theme. The download needs the instance’s cluster (OAuth) credentials; it is skipped while those are unavailable.
  4. Instances activate the theme. Once node/theme/ holds a valid theme, the instance switches its active theme path to it, so the cluster branding is applied.

Because refreshes are version-based, bump the theme’s version.txt when you change branding — instances only re-download when the Portal version differs from their installed one.

Seeding the Theme from a URL

Set PHOTOPRISM_THEME_URL on the Portal to an archive (.zip) that contains the theme files at its root (including app.js). The Portal installs it on first start when no theme is present:

services:
  portal:
    image: photoprism/portal:latest
    environment:
      PHOTOPRISM_NODE_ROLE: "portal"
      PHOTOPRISM_THEME_URL: "https://cdn.example.com/themes/acme-theme.zip"

Leave PHOTOPRISM_THEME_URL empty to disable the auto-install and manage the theme directory yourself. See Config Options for the related settings.

Pulling the Theme Manually

You can download the current Portal theme to an instance from the command line. The pull command installs into config/theme by default, or a directory you pass with --dest. If only a join token is provided, it registers the node first to obtain credentials, then downloads:

photoprism cluster theme pull --dest /photoprism/storage/config/node/theme

PhotoPrism® Documentation

For more information on specific features, services and related resources, please refer to the other documentation available in our Knowledge Base and User Guide: