← Back to Framewrk

Installing Framewrk

One container, one folder for its database, port 8770. Everything else is set up in the console after it starts — there is nothing to configure first.

Pick your platform. Each section ends with the thing that actually catches people out there.

Before you start, you need an Aura and/or Nixplay account with at least one frame on it. For scheduled library sync, you also need a reachable PhotoPrism and/or Immich instance. Direct sends from the iOS app do not require a photo library; they require your Framewrk server to be reachable over HTTPS.

After it starts, on every platform: open http://<host>:8770, find the one-time admin password in the container log, sign in, and choose your own password when asked.


Linux

mkdir framewrk && cd framewrk
curl -O https://raw.githubusercontent.com/smichalczyk/framewrk/main/compose.yaml

Set PUID and PGID in compose.yaml to your own user, and TZ to your timezone:

id -u    # -> PUID
id -g    # -> PGID

Then:

docker compose up -d
docker compose logs | grep -A2 "Admin password"

Reaching a photo source. If PhotoPrism or Immich is a container on the same host, put both on one network and address it by name (http://photoprism:2342 or http://immich:2283) rather than a LAN address that can change. Uncomment the networks block at the bottom of compose.yaml. If Framewrk still cannot see it, network_mode: host usually settles it — Linux only, and it ignores ports, so the console lands on the host's 8770 directly.

The catch: leave PUID/PGID unset and everything in data/ ends up owned by root. It works, right up until the day you want to read your own backup.


macOS

Needs Docker Desktop or Colima.

mkdir framewrk && cd framewrk
curl -O https://raw.githubusercontent.com/smichalczyk/framewrk/main/compose.yaml

Delete the PUID and PGID lines. Docker Desktop's VM maps ownership for you, and setting them here causes the mismatch it is meant to prevent. Set TZ.

docker compose up -d
docker compose logs | grep -A2 "Admin password"

The catch: a Mac that sleeps stops syncing. Framewrk catches up on the next run rather than losing anything, but if the frames matter, run it somewhere that stays awake.


Windows

Needs Docker Desktop with the WSL 2 backend.

In PowerShell:

mkdir framewrk; cd framewrk
curl.exe -O https://raw.githubusercontent.com/smichalczyk/framewrk/main/compose.yaml

Delete the PUID and PGID lines, set TZ, and change the volume to a named volume:

    volumes:
      - framewrk-data:/data

volumes:
  framewrk-data:
docker compose up -d
docker compose logs | Select-String -Context 0,2 "Admin password"

The catch, and it is a real one: do not bind-mount a Windows path such as C:\Users\you\framewrk\data into /data. SQLite's write-ahead log needs file locking that does not work reliably across the Windows/WSL filesystem bridge, and the failure does not look like a filesystem problem — it looks like a corrupted database, three weeks later. A named volume lives inside the Linux VM and has none of that. To get a backup out:

docker exec framewrk sqlite3 /data/sync.db ".backup /data/backup.db"
docker cp framewrk:/data/backup.db .

Synology

DSM 7.2 or newer, with Container Manager installed from Package Center.

1. Find your user's numbers

This is the step everyone skips, and it is why most Synology support threads exist. Synology's first user account is 1026, not 1000, and the primary group users is 100.

Enable SSH (Control Panel → Terminal & SNMP → Enable SSH service), then:

ssh you@your-nas
id
# uid=1026(you) gid=100(users) ...

Note both numbers and turn SSH back off if you had it off.

2. Make the folders

In File Station, inside the docker shared folder (Container Manager creates it), make framewrk, and inside that, data.

3. Create the project

Container Manager → Project → Create.

Paste this, with your two numbers and your timezone:

services:
  framewrk:
    image: ghcr.io/smichalczyk/framewrk:latest
    container_name: framewrk
    restart: unless-stopped
    ports:
      - "8770:8770"
    environment:
      - PUID=1026
      - PGID=100
      - TZ=Europe/London
    volumes:
      - /volume1/docker/framewrk/data:/data

Next → Next → Done. Container Manager pulls the image and starts it.

4. Sign in

Container Manager → Container → framewrk → Log, find the admin password, then open http://<nas-ip>:8770.

The catches:


QNAP

QTS or QuTS hero with Container Station 3.

1. Find your user's numbers

Do not use admin — it is uid 0. Use a normal share user. Over SSH:

id your-user
# uid=1000(your-user) gid=100(everyone) ...

2. Make the folders

In File Station, inside the Container share (Container Station creates it), make framewrk, and inside that, data.

Check the real path in File Station's properties — it is usually /share/Container/... but on some models /share/CACHEDEV1_DATA/Container/.... Getting this wrong silently creates an empty directory, and the database starts fresh on every boot.

3. Create the application

Container Station → Applications → Create. Name it framewrk and paste:

services:
  framewrk:
    image: ghcr.io/smichalczyk/framewrk:latest
    container_name: framewrk
    restart: unless-stopped
    ports:
      - "8770:8770"
    environment:
      - PUID=1000
      - PGID=100
      - TZ=Europe/London
    volumes:
      - /share/Container/framewrk/data:/data

Click Validate, then Create.

4. Sign in

Containers → framewrk → Logs for the password, then http://<nas-ip>:8770.

The catches:


Unraid

From Community Applications

Search Apps for Framewrk and install. The template fills in the port, the /data path, PUID, PGID and TZ for you — set your timezone and press Apply.

By hand

Docker → Add Container, then:

Field Value
Name framewrk
Repository ghcr.io/smichalczyk/framewrk:latest
Network Type bridge
WebUI http://[IP]:[PORT:8770]
Port container 8770 → host 8770
Path container /data → host /mnt/user/appdata/framewrk
Variable PUID = 99
Variable PGID = 100
Variable TZ = your timezone

Sign in

Click the container → Logs for the admin password, then the WebUI link.

The catch: 99/100 is Unraid's nobody/users, which is what the rest of your appdata uses. Leaving them unset makes everything in the folder root-owned, and Unraid's own backup plugins then cannot read it.


TrueNAS SCALE

24.10 (Electric Eel) or newer, where the apps engine is plain Docker.

1. Make a dataset

Storage → Datasets → select your pool → Add Dataset, named framewrk. Note the path it gives you, e.g. /mnt/tank/apps/framewrk.

Then Datasets → framewrk → Permissions → Edit: set Owner apps and Group apps (both UID/GID 568), and apply recursively.

2. Install

Apps → Discover Apps → the menu at the top right → Install via YAML.

services:
  framewrk:
    image: ghcr.io/smichalczyk/framewrk:latest
    container_name: framewrk
    restart: unless-stopped
    ports:
      - "8770:8770"
    environment:
      - PUID=568
      - PGID=568
      - TZ=America/New_York
    volumes:
      - /mnt/tank/apps/framewrk:/data

Save.

3. Sign in

Apps → Installed → framewrk → Logs for the password, then http://<truenas-ip>:8770.

The catches:

Immich setup

Framewrk supports Immich 3.1 or newer. In the Source screen, enter your Immich URL and create an API key with asset read/download and album read access. You may configure Immich by itself or alongside PhotoPrism.

The shared Quality setting uses Immich's optional star rating. 0 skips the rating filter; 1 through 5 select that rating and above. Enable and assign ratings in Immich under User Settings → Features → Rating.


Something went wrong

Still stuck? Open a discussion with your platform, the version from Settings → About, and the relevant lines from Logs.

Send directly from iPhone or iPad

The Framewrk iOS beta can send photos without PhotoPrism or Immich. Connect Aura or Nixplay and configure a frame in the console, then connect the app to your HTTPS server address using your console password. The app stores a revocable device token in Keychain.

Choose up to 50 photos in the app, or select photos in Photos or another app and choose Share → Framewrk. Pick the frames and send. Background uploads continue after the sheet closes; Recent confirms when your server receives each photo. Nixplay sends use configured albums and disclose other frames playing them.

Frame orientation rules apply. Direct photos stay on the frames independently of library selection. The console’s Direct sends page shows delivery history and orientation skips, and Settings → Connected devices revokes a device. Full images are retained for retries for up to 7 days; previews last 90 days. Allow at least 17 MiB request bodies through your HTTPS reverse proxy.