-
Notifications
You must be signed in to change notification settings - Fork 0
Self Hosting
- Docker and Docker Compose
- A folder of photos and videos accessible as a host path
- Optional: Intel, NVIDIA, or AMD GPU for hardware-accelerated video transcoding
# 1. Copy the environment template
cp src/.env.example src/.env
# 2. Fill in your values (see Configuration reference below)
# Required: DB_PW, WORKER_USER
# 3. Edit docker-compose.yml — replace the placeholder NAS path:
# /path/to/your/nas → your actual mount path
# 4. Start all containers
docker-compose -f src/docker-compose.yml up -dThe API is available on port 8080.
A single Worker handles all users by default. No extra configuration is needed — the Worker discovers all registered users and their NAS paths from the database on startup, then crawls each in round-robin order.
Each user's NAS folder must be mounted into the single Worker container. If all users share one NAS root, one mount is enough:
anichron-worker:
volumes:
- /nas:/data/originals:ro # all users' folders under one root
- ${BASE_PATH}/anichron/proxy_cache:/data/proxies:rwIf users have separate NAS roots, mount them as subdirectories:
anichron-worker:
volumes:
- /nas/alice:/data/originals/alice:ro
- /nas/bob:/data/originals/bob:ro
- ${BASE_PATH}/anichron/proxy_cache:/data/proxies:rwDedicated Worker for one user (optional): Set WORKER__USER on a Worker container to pin it to a specific user. This is useful when one user has a very large library and needs a dedicated processor:
anichron-worker-alice:
environment:
- WORKER__USER=alice@example.com
volumes:
- /nas/alice:/data/originals:ro
- ${BASE_PATH}/anichron/proxy_cache:/data/proxies:rwEach NAS path must belong to exactly one user — sharing a path between users is not supported. See Deployment Decisions and ADR-12 for the rationale.
The Worker detects available hardware at startup and selects the best encoder automatically:
- Intel QuickSync (
h264_qsv) — requires/dev/dripassthrough - NVIDIA NVENC (
h264_nvenc) — requires NVIDIA runtime - AMD AMF (
h264_amf) — requires AMD GPU - Software (
libx264) — always available, no hardware required
If you have no Intel GPU, remove the devices block from the worker service in docker-compose.yml. The Worker falls back to software encoding without error. See Deployment Decisions for details.
The API applies a built-in sliding-window rate limit (10 requests / 60 seconds per IP) to the login and registration endpoints. For stronger protection — especially against distributed attacks — configure rate limiting at your reverse proxy as well.
If your UI and API run on different origins (e.g. UI on app.example.com, API on api.example.com), configure the allowed origins on the API container. The API uses indexed ASP.NET Core array binding:
environment:
- Cors__AllowedOrigins__0=https://app.example.com
- Cors__AllowedOrigins__1=https://localhost:3000 # optional second originWhen at least one origin is configured:
- The API sends the correct
Access-Control-Allow-Originheader - The refresh token cookie switches from
SameSite=StricttoSameSite=Noneautomatically (required for cross-origin cookie sending; stillHttpOnlyandSecure) -
AllowCredentialsis enabled on the CORS policy so the browser attaches the cookie
Leave all Cors__AllowedOrigins__* unset when using a reverse proxy on the same origin — the API defaults to SameSite=Strict which provides stronger CSRF protection and does not require a CORS policy.
On first startup, if the database contains no users, the API automatically creates a bootstrap admin account. The username and password are read from environment variables:
| Variable | Default | Notes |
|---|---|---|
BOOTSTRAP_ADMIN_USERNAME |
admin |
Stored lowercased |
BOOTSTRAP_ADMIN_PASSWORD |
admin |
Must be changed on first login |
If either variable is absent, the defaults (admin / admin) are used and a warning is logged. The account is created with MustChangePassword = true regardless.
Recommended setup:
environment:
- BOOTSTRAP_ADMIN_USERNAME=yourname
- BOOTSTRAP_ADMIN_PASSWORD=a-strong-random-passwordRemove or clear both variables after the admin account is created — they are only read when no users exist.
If you lose access to the admin account, set ADMIN_RESET_PASSWORD on the API container and restart:
environment:
- ADMIN_RESET_PASSWORD=new-passwordOn startup the API resets the password and sets MustChangePassword = true. A warning is logged. Remove the variable immediately after logging in — it is applied on every restart while set.
If exactly one admin account exists, it is reset automatically.
If multiple admin accounts exist, you must also set ADMIN_RESET_USERNAME to identify the target. Without it the API logs an error and skips the reset entirely:
environment:
- ADMIN_RESET_PASSWORD=new-password
- ADMIN_RESET_USERNAME=aliceOn first startup the API generates configuration/app.ini inside the container's working directory. The JWT signing secret is auto-generated and written there — no manual secret generation is required.
Any setting listed below can be placed in app.ini instead of (or alongside) environment variables. Environment variables always take precedence over app.ini values.
| Variable | Description |
|---|---|
DB_PW |
PostgreSQL password for the anichron_admin database user |
WORKER__USER |
Email or username of the user this Worker instance processes |
| Variable | Default | Description |
|---|---|---|
Cors__AllowedOrigins__0 |
(unset) | First allowed UI origin (e.g. https://app.example.com). Add __1, __2, … for additional origins |
Jwt__AccessTokenMinutes |
15 |
Access token lifetime in minutes |
Jwt__RefreshTokenDays |
60 |
Refresh token lifetime in days |
BOOTSTRAP_ADMIN_USERNAME |
admin |
Username for the bootstrap admin created on first startup (empty DB only) |
BOOTSTRAP_ADMIN_PASSWORD |
admin |
Password for the bootstrap admin. Sets MustChangePassword = true
|
ADMIN_RESET_PASSWORD |
(unset) | Resets the target admin's password on every startup until removed |
ADMIN_RESET_USERNAME |
(unset) | Required when multiple admin accounts exist; identifies which admin to reset |
| Variable | Default | Description |
|---|---|---|
WORKER__USER |
(required) | Email or username of the user this Worker belongs to |
WORKER__ROOT |
/data/originals |
Root path inside the container to scan for media |
WORKER__CRAWL_INTERVAL_HOURS |
4 |
How often to run a full NAS crawl, in hours |
WORKER__MAX_CONCURRENT_FILES |
4 |
Maximum number of files to process in parallel |
Anichron
Architecture
- Solution Overview
- Database Architecture
- Architecture Diagrams
- Deployment Decisions
- Engineering Decisions
- Versioning and Releases
Tracking