A self-hosted container that watches an "incoming" photo and video folder, sorts everything into a dated library, fixes the timestamps and GPS that phones and drones get wrong, tells Immich to rescan, and gives you a web page to run and configure all of it.
One image, one container, one web UI on port 8420. Everything it keeps (configuration, databases, logs, backups) lives in a single volume you mount.
Built and tested on Synology DSM 7.2 (Container Manager), and it runs on any Linux host with Docker. Images are published for
linux/amd64andlinux/arm64.
Year / Year_Month / Year_Month_Day, using the date the photo or video was
really taken (falling back to the file date). Runs when files arrive, on a
schedule, or when you click Run Now.desktop.ini and the like, from a list you
edit), and clears thumbnail caches.stable release — informational only,
it never pulls or applies anything by itself.| Tag | What it is |
|---|---|
stable | The most recent release that has been checked and promoted. Use this. It only changes when a new release is promoted. |
vX.Y.Z | A specific release (for example v1.2.0). Never moves. Pin one to control exactly when you upgrade, or to roll back. |
latest | Rebuilt daily from the newest code, with a fresh base image. A rolling tag: it can change under you. |
YYYY.MM.DD | A dated build, for reference. |
To see exactly what a container was built from:
docker exec photo-pipeline cat /app/BUILD_INFO
Create two folders on the host: one for the app's own data, and your media root (the folder that contains both your incoming folder and your library).
services:
photo-pipeline:
image: mallison01/photo-pipeline:stable
container_name: photo-pipeline
restart: unless-stopped
init: true
ports:
- "8420:8420"
environment:
ADMIN_USERNAME: "admin"
ADMIN_PASSWORD: "" # blank: a random one is generated and printed to the logs once
PUID: "1000" # the user that should own files this container writes
PGID: "1000"
volumes:
- /path/to/photo-pipeline-data:/data # all app state: config, databases, logs, backups
- /path/to/your/media:/media # your library root, read/write
mkdir -p /path/to/photo-pipeline-data # Docker will not create a missing host folder
docker compose up -d
docker compose logs -f # first boot prints the admin password, once
Open http://<host>:8420, sign in, and set your own password. Then:
/media is browsable.The first start needs outbound internet access once, to install a time-zone helper into the data volume. It is kept across restarts and updates.
Equivalent docker run:
docker run -d --name photo-pipeline --init --restart unless-stopped \
-p 8420:8420 \
-e ADMIN_USERNAME=admin -e PUID=1000 -e PGID=1000 \
-v /path/to/photo-pipeline-data:/data \
-v /path/to/your/media:/media \
mallison01/photo-pipeline:stable
| Variable | Default | Purpose |
|---|---|---|
ADMIN_USERNAME | admin | First-login account name (first boot only). |
ADMIN_PASSWORD | (blank) | First-login password. Blank generates a random one, printed to the logs once. Only used on the very first boot; change it in the UI afterward. |
PUID / PGID | 1000 / 1000 | User and group that own files the container creates or changes in your library. On a NAS, use the numbers of the account that owns your media (ls -ln on the folder). |
EXTRA_GROUPS | (blank) | Comma-separated extra group IDs. Synology: needed when your share's ACL grants access to a named group (see below). |
TRUSTED_PROXY_HOPS | 0 | Set to 1 if a reverse proxy in front of the container terminates TLS. |
FORCE_INSECURE_COOKIES | 1 | The default lets the UI work over plain http://. With HTTPS in front, set it to 0 so the session cookie is marked Secure. |
Everything else (Immich and Dawarich addresses and keys, schedules, time zone, SMTP, user accounts, login lockout, session length) is configured in the web UI, not through environment variables.
| Volume | Contents |
|---|---|
/data | All app state: pipeline configuration files, the app's databases, run logs, and backups. Back this one folder up and you have everything. |
/media | Your photo and video library, mounted read/write. |
Three things are specific to a Synology NAS:
PUID / PGID. Read them from the folder that owns your media:
ls -ln /volume1/Shares/YourShare
EXTRA_GROUPS. If the app's folder browser shows nothing for folders
you know exist, your share's ACL probably names a group instead of matching
your PGID. Look at the ACL, then look the group up:
/usr/syno/bin/synoacltool -get "/volume1/Shares/YourShare"
grep '^administrators:' /etc/group
EXTRA_GROUPS (on DSM, administrators is
usually 101).synoacltool. That needs
these three mounts under volumes::
- /usr/syno:/usr/syno:ro
- /usr/lib:/host-lib:ro
- /lib:/host-lib2:ro
acl_restore_failed in Reports, and other accounts (File Station, SMB) may
not get the folder's permissions on files the pipeline moved. Skip these on
a non-Synology host.Nothing updates itself. To move to a newer release:
docker compose pull
docker compose up -d
Your data volume carries over, and the new version upgrades its own databases and configuration on start. On the first start of a different build, the container takes a full backup before it changes anything. The Backups page then names that archive, so you always have a restore point.
Rolling back: pin the previous tag (for example
image: mallison01/photo-pipeline:v1.1.0) and docker compose up -d. Updates
so far only add to your data, which an older version simply ignores, so this is
usually enough. If it is not, restore the pre-upgrade backup from
Settings → Backups → Restore.
Settings → Backups makes a single archive of every pipeline's
configuration, databases and logs, the app's own accounts database and its
settings. You can back up on demand, on a schedule (chosen days, at a set time
or repeating), download the file, and restore it, whole or in parts, on this or
another machine. Backups are stored in /data/backups by default; point them at
another volume so a single disk failure can't take the data and its backups
together. The page reminds you until you do.
Backup archives contain your pipeline configuration, including any Immich and Dawarich API keys and the SMTP password. Store them accordingly.
TRUSTED_PROXY_HOPS=1 and FORCE_INSECURE_COOKIES=0).PUID/PGID./data. Protect
that volume.docker run./data, and your media folder for /media.The application source is developed privately; this image is what is published.
The exact commits an image was built from are in /app/BUILD_INFO.
Content type
Image
Digest
sha256:ebe4f750d…
Size
74.3 MB
Last updated
about 11 hours ago
docker pull mallison01/photo-pipeline