Before you begin
Smriti does not ship pre-built container images yet — every install path on this page
builds the app, worker and ML-worker images from source. That means
the first install takes longer than a normal docker pull would (roughly a
few minutes on a modest server), and it means you need enough free disk for a source
checkout, the built image layers, and your own library on top.
You will want, regardless of path:
- A Linux server (or TrueNAS SCALE, which is Linux underneath) with at least ~5 GB free for the build, separate from your media.
- An existing folder of photos/files you want Smriti to manage — it reads your library in place, it does not import it into a separate store.
- A port to publish the web UI on.
30078is Smriti's own registered default; any free port works.
Docker (recommended today)
This is the path that works right now, on any server: a plain docker compose
stack, built from a git checkout, with the same network-isolation topology the maintainer
runs in production (the ML worker and the database have no route to the internet — only
the web container publishes a port).
Install
The one-line installer (deploy/install.sh) does the whole thing: checks the
runtime, disk space and port, asks before doing anything, clones the source, generates
random database credentials, builds the images, runs migrations, activates Postgres
row-level security (the Vault boundary), and starts the stack.
curl -fsSL https://get.smriti.cloud/install.sh | sh
Useful flags (all also settable as SMRITI_* environment variables — see the
script's own header comment for the full list):
| Flag | Default | What it controls |
|---|---|---|
--media-root=PATH | prompted | the existing folder Smriti manages, mounted read-write |
--data-dir=PATH | <install-dir>/data | where Postgres/Redis/app data persist |
--port=N | 30078 | host port the web UI is published on |
--install-dir=PATH | $HOME/smriti | where the source checkout and generated compose file live |
--ref=NAME | main | git branch/tag to build |
--yes | off | skip the confirmation prompt (for scripted installs) |
Prefer to read every line first, as you should for anything piped into a shell?
You will be able to — the source is not public yet, so the script is not readable
online today. It is plain POSIX sh, no obfuscation, and it prints exactly
what it is about to do before it does it. The Compose file it generates
(<install-dir>/docker-compose.yml) is also plain text once it exists,
and re-running the script is how you upgrade — it never regenerates an existing install's
secrets or deletes anything.
Configuration reference
Every setting the generated stack actually uses. Most are already correct defaults; you only need to touch the ones marked otherwise.
| Variable | Set on | Purpose |
|---|---|---|
DATABASE_URL | app, arq-worker | connects as the dedicated, non-superuser smriti_app role so Postgres row-level security enforces the Vault boundary. Set for you by the installer. |
MEDIA_ROOTS | app, arq-worker | comma-separated list of paths Smriti indexes (the installer wires your one media root to /media/library). |
ML_WORKER_URL | app, arq-worker | internal-only URL of the face/CLIP worker. Config, never user input — leave it alone. |
SESSION_COOKIE_SECURE, VAULT_COOKIE_SECURE, SHARE_COOKIE_SECURE | app | the installer sets these to false for a plain-HTTP LAN install. If you put a TLS-terminating reverse proxy in front, set all three to true — otherwise a Secure cookie is silently never sent back over HTTP and login breaks. |
FORWARDED_ALLOW_IPS | app | which exact peer IP to trust X-Forwarded-* headers from. Pinned to the bundled nginx container's fixed address by default; never set it to *. |
MAX_CONCURRENT_MEDIA_JOBS, FFMPEG_THREADS, VIPS_CONCURRENCY | app, arq-worker | bound how much heavy media work (thumbnails, transcodes, face scans) can run at once. Defaults are tuned for a modest, fanless box — raise them on beefier hardware. |
RECOGNITION_MODEL_ID, ORT_THREADS, CLIP_MODEL_ID | ml-worker | which face/CLIP model packs to serve and how many inference threads to use. Changing the recognition model later requires re-embedding every face. |
SMRITI_MEDIA_ROOT, SMRITI_DATA_DIR, SMRITI_PORT | the installer's own .env | the three values you were prompted for. Safe to hand-edit before the next upgrade run. |
Storage roots & data directory
The installer wires exactly one media root today. Additional storage roots (Smriti supports one per user under the hood) are an admin-panel feature you can add once the app is running, after mounting the extra path into the containers yourself.
Under the data directory, four subfolders are created and must persist across restarts and upgrades — none of them should be deleted or excluded from your backups:
postgres/— the database: people, faces, albums, tags, favourites, every manual correction.redis/— the background job queue's own storage.config/— reserved for persistent app configuration.appdata/— generated thumbnail and video-transcode caches. Regenerating either is heavily CPU-bound, so wiping this is expensive, not dangerous.
Upgrade & rollback
Upgrade: re-run the installer (same command, same --install-dir).
It reuses your existing secrets and paths, fetches the chosen ref, rebuilds the images,
re-runs migrations (a no-op if already at head), and restarts.
Rollback: Smriti's database migrations are forward-only — there is no
automatic "undo" for a schema change, because some migrations discard data on the way
down. The safe rollback is: take a Postgres backup before upgrading (the
pg_dump-based scripts in this repo's deploy/ directory are the
reference for doing that by hand — they are not yet wired into the one-line installer
itself), then if an upgrade goes wrong, restore that backup and check the source out
back to the previous ref (git -C <install-dir>/src checkout <old-ref>)
before rebuilding. This is a manual process today, not a one-command rollback.
Verify it worked
curl http://<host>:<port>/api/healthshould return a healthy response.- Open
http://<host>:<port>/in a browser — the first visit walks you through first-run setup, creating the super-admin account. cd <install-dir> && docker compose logs -fshows all six services starting; the ML worker's healthcheck has a generous 60-second start period, so give it a minute.
Podman
deploy/install.sh
actively detects a Podman-only host (including distros where the docker
command itself is a Podman compatibility shim) and refuses cleanly with an explanation,
rather than half-installing something that cannot yet reproduce Smriti's network-isolation
guarantees.
TrueNAS SCALE
truenas-app/ix-dev/community/smriti/), and
everything below is verified against that packaging's real questions.yaml —
but the install itself cannot complete today, because Smriti has no published container
images for TrueNAS's app system to pull (it only knows how to run named images, unlike
Docker Compose it cannot build from source). This is a tracked, known gap, not an
oversight. The sections below describe the form as it exists now, so it is accurate the
day images ship. TrueNAS users who want to run Smriti today should use the
Docker path above on any Linux host.
Adding the catalogue
Once images are published, Smriti's catalogue entry is added the same way any custom
TrueNAS SCALE catalogue is: Apps → Discover Apps → the settings (gear) icon →
Manage Catalogs → Add Catalog, pointing at this repository's URL with the
community train. If Smriti is later accepted into TrueNAS's own official
apps catalogue, it will simply appear under Discover Apps with no separate step.
The install form, setting by setting
The form is organised into six groups. Everything below matches the packaging's
questions.yaml exactly.
Smriti Configuration
| Setting | Recommendation |
|---|---|
| Timezone | your server's real timezone — used for schedules and timestamps. |
| Database Owner Password / Application Database Password | two different strong, random passwords (16+ characters). The owner runs migrations; the app connects as the separate, non-superuser role so row-level security actually enforces the Vault boundary. Save both — the owner password is needed to restore a backup. |
| Redis Password | a strong, random password for the bundled job queue. |
| Take a database backup before every deployment | leave ON. alembic downgrade is not a safe undo for Smriti; this dump is the real rollback path, and a failed dump stops the deployment before it touches anything. |
| Face Recognition Model | buffalo_l (the recommended default). Changing it later re-embeds every face. |
| ML Inference Threads / Concurrent Media Jobs / FFmpeg Threads | leave the defaults (2 / 1 / 1) on a fanless or passively-cooled NAS; raise only on hardware with real sustained cooling. |
| Maximum Disk/CPU Temperature | defaults (55°C / 85°C) are conservative and match the reference hardware; lower them if your box runs hotter than expected. |
User and Group Configuration
Leave both at 568 (TrueNAS SCALE's built-in apps account)
unless your media datasets are owned by a different uid/gid — Smriti's containers need
to run as whichever account already owns those files.
Network Configuration
WebUI Port: 30078 is Smriti's own registered default; change it only if it collides with something else already using that port.
Storage Configuration
| Setting | Recommendation |
|---|---|
| Media Roots | at least one is required. Point each at an existing dataset on a data pool (never the boot pool), mounted read-write — the file manager, trash and deduplicator all need to write. |
| Derived Data, Application Config, Postgres Data, Pre-upgrade Backups | leave as ixVolume (TrueNAS creates and manages the dataset) unless you specifically need a hand-picked path. Postgres Data and Backups are the two that matter most for disaster recovery — put them where your snapshot schedule already covers them. |
Datasets & NFSv4 ACLs
TrueNAS SCALE pools commonly use NFSv4 ACLs in restricted mode, where a plain
chmod fails outright. You do not need to fight this by hand: every storage
entry on the install form has an Enable ACL toggle, and when it is on,
TrueNAS sets the correct NFSv4 ACL for the container's uid/gid itself through its own
filesystem API — the same mechanism an administrator would otherwise drive manually
(TrueNAS's midclt filesystem.setacl). Turn it on for any dataset you are
creating fresh for Smriti; leave it off only for a path whose permissions you have
already set up correctly outside the form.
First run & verifying it worked
- The Apps page shows the app as Running once every container passes its healthcheck — the ML worker's can take up to a minute on first start.
- Open
http://<truenas-ip>:<chosen-port>/— the first visit walks you through first-run setup, creating the super-admin account. - The app's own logs (Apps → Smriti → Logs, per container) are the first place to look if a container never reaches Running.