PhotoStructure for Docker
These are instructions for advanced users, wanting to run PhotoStructure for Servers via Docker.
π Background reading π
-
You should read the pros and cons of both Docker and Node editions before continuing.
-
Docker-compose may be easier to use than these instructions, which use
docker
directly, because:- upgrades are automatic
- adding and removing volumes to scan into your library is easy
The downside is that it’s one more software package that you need to install, and it may not be available on your host machine. The instructions for docker-compose are here.
-
If you want imported volumes in your library to be portable across machines, please read what’s a βvolume?β (hint: skip to the last section and create
.uuid
files for all the volumes you import). -
Don’t miss the PhotoStructure user guide!
π₯οΈ System requirements π
The docker images run under linux x64 Docker hosts.
PhotoStructure containers require at least 2GB of RAM during imports. You’ll see
ENOMEM
errors if you run within a 1GB container. PhotoStructure imports run
faster when more RAM and more CPU are allocated to the container.
The docker image is built on a recent CPU with SSE4
and AVX
extensions: CPUs
that are less than a decade old should work fine, but if your server is running
an older processor (like an AMD Phenom II), the image may not spin up.
π Install Docker π
Follow these instructions for docker. If you’re on Ubuntu, follow these steps.
ποΈ Bind-mount setup use cases π
PhotoStructure for Docker needs at least one bind-mount set up in order to store your library and metadata.
π³ I’ve got a big local disk for PhotoStructure π
You only need one bind-mount: /ps/library
#!/bin/bash
PSLIBRARY="$HOME/PhotoStructure-test" # < CHANGE THIS LINE
mkdir -p "$PSLIBRARY"
IMG="photostructure/server:alpha"
docker pull $IMG
docker run \
--name photostructure \
--detach \
--restart unless-stopped \
--stop-timeout 120 `# < gives PhotoStructure 2 minutes to shut down cleanly.` \
--publish 1787:1787 \
-e TZ="${TZ:-$(cat /etc/timezone)}" \
-e PUID="$(id -u)" \
-e PGID="$(id -g)" \
-v "$PSLIBRARY":/ps/library \
-v "$HOME"/Pictures:/pictures/ `# < EXAMPLE: CHANGE THIS LINE` \
"$IMG"
And then open http://localhost:1787 and finish setup.
π°οΈ I want to store my library on a remote disk π
- Set up
/ps/library
to point to your remote disk mountpoint - Set up
/ps/tmp
to point to a local disk
Use the comprehensive example, and edit to taste.
ποΈ I want full control over PhotoStructure π
- You must provide the
/ps/library
bind-mount - You can add any and all of the other bind-mounts as you see fit. If any are
omitted, a default path within
/ps/library
will be used instead.
The comprehensive example includes examples for all of PhotoStructure’s /ps/...
bind-mounts.
π I have photos from a bunch of different directories I want to import π
For every directory of photos and videos, bind-mount them into any directory into your container
π I don’t want to give write access to PhotoStructure π
PhotoStructure supports readonly bind-mounted directories
ποΈ Docker bind-mount setup π
PhotoStructure has 4 directories it writes information into, and they can all
be configured to suit your taste. Only /ps/library
is required: all other
/ps/...
directories have alternative defaults if they are missing (thanks to “docker easy-mode”)
The following examples set up PhotoStructure to scan both a mounted external
hard drive at /mnt/Photos backup
and all files found in $HOME/Pictures
.
You’ll want to edit these examples to be relevant to your setup.
ποΈ /ps/library
π
- This is where your PhotoStructure Library will be stored.
- This bind-mount is required.
- It must be readable and writable by the PhotoStructure container, and have sufficient free space.
- If there is no
/ps/tmp
bind-mount, this must be a local disk.
/ps/tmp
π
This is PhotoStructure’s “scratch” directory.
/ps/tmp
must be on a local disk.- This volume should have at least 16-32 GB free.
- If you’ve got an SSD, use that: this directory will see a lot of reads and writes.
- If you can, pick (or create) a volume that doesn’t have data integrity protection, on-the-fly compression, or on-the-fly file de-duplication. This will make imports faster as well as reduce system load.
- PhotoStructure automatically prunes old and unused files from the scratch directory: it shouldn’t grow linearly with your library.
If this bind-mount doesn’t exist, PhotoStructure will use
/ps/library/.photostructure/cache
instead.
/ps/config
π
This is where PhotoStructure stores system settings.
If this bind-mount doesn’t exist, PhotoStructure will use
/ps/library/.photostructure/docker-config
instead.
/ps/logs
π
This is where PhotoStructure stores logfiles. Normally PhotoStructure should be
mostly quiet, unless you “send recent logs,” or set
PS_LOG_LEVEL
to something like info
.
PhotoStructure deletes logfiles older than 1 week automatically.
If this bind-mount doesn’t exist, PhotoStructure will use
/ps/library/.photostructure/logs
instead.
ποΈ External directories to import π
-
To add directories you want to import into your PhotoStructure library, simply add them as bind volumes to your container.
-
It may be easiest to use a root directory, like
/photos
or/pictures
or/albums
. -
Don’t bind-mount into any previously-existing file or directory, like
/etc
,/bin
,/opt
, or/usr
.
-
If you would like to ensure that PhotoStructure doesn’t change anything in the directories you import, you can use read-only bind-mounts, with a couple caveats:
-
You will need to drop a .uuid file in the root of these volumes if you want your library to be portable.
-
This will prevent PhotoStructure from being able to save any metadata changes to these files, so rotation and other edits will not be persisted.
-
Note that
/ps/library
must be read-writable, so remember to bind-mount your existing photos and videos to a different directory.
-
π Directories to omit π
PhotoStructure will import all mounted directories recursively. PhotoStructure
respects NoMedia
files, and will exclude those directories from being
imported. (learn more)
π§ͺ Advanced settings π
PhotoStructure has a bunch of settings that are only configurable outside of the PhotoStructure UI.
Docker users may find it’s easiest to use environment variables to override settings. Read more about PhotoStructure’s use of environment variables.
Running PhotoStructure for Docker as a non-root user π
πΊ Why not run as root? π
Dockerized applications run as root by default. If PhotoStructure runs as root, the files in your library will be owned by root, which can cause permission issues with the original files that PhotoStructure copies into your library.
Running PhotoStructure as a non-root user is also considered more secure.
How do I specify the userid and groupid? π
PhotoStructure uses linuxserver.io-style PUID
/PGID
environment
variables.
Default PUID/PGID values π
If $PUID
or $PGID
is not set, PhotoStructure tries to be clever and looks
at the current owner of the system settings.toml
or library settings.toml
.
If both of those are missing, we default to 1000
for both $PUID
and
$PGID
, which are the default userid and groupid given to the first
non-system user (at least in Ubuntu and Fedora).
Run as root π
If you want PhotoStructure to run as root, set PUID=0
and PGID=0
.
Why is PhotoStructure running as node
? π
Rather than creating a new user in PhotoStructure’s docker container, we just
change the userid and groupid of node
to match $PUID
and PGID
.
chown
on startup π
PhotoStructure’s docker-entrypoint.sh
script will chown -R
your
library support directories if the current owner doesn’t match $PUID
, so
that after we drop root privileges to be node
, we still can read and write to your library.
Disable these chown
calls by setting the environment variable PS_NO_PUID_CHOWN=1
.
More PUID/PGID info π
- Here is the feature request in the forum
- Here is a file permission issue due to inconsistent PUID/PGID use
π‘ Networking setup π
-
The
PS_HTTP_PORT
environment variable defaults to 1787 but can be changed to a different port to suit. -
If you set up a reverse HTTPS proxy via nginx and find that dynamic updates (like the progress status) aren’t showing up, add this to your nginx config:
proxy_http_version 1.1;
proxy_set_header Connection "";
βοΈ Example Docker setup π
This bash shell script can be saved to make future upgrades easier. See the docker documentation for more details.
#!/bin/bash
# Howdy, and welcome to PhotoStructure!
# Please review every "CHANGE THIS LINE" below.
# For more details, check out
# <https://photostructure.com/server/photostructure-for-docker/>
PSLIBRARY="$HOME/PhotoStructure" # < CHANGE THIS LINE
mkdir -p "$PSLIBRARY"
# This must be a fast, local disk (preferably an SSD) with many gigabytes free.
# It doesn't have to be in /tmp.
PSTMP="/tmp/ps-$USER"
mkdir -p -m 0700 "$PSTMP"
# This directory stores your "system settings". This directory must not be
# the same as the one used by PhotoStructure for Desktops.
PSCONFIG="$HOME/.config/PhotoStructure-docker"
mkdir -p "$PSCONFIG"
# This directory stores PhotoStructure logfiles.
PSLOGS="$HOME/.config/PhotoStructure/logs"
mkdir -p "$PSLOGS"
# TZ is only used as a backstop.
# Using `tzselect` might be helpful if you don't have TZ set.
TZ=${TZ:-$(cat /etc/timezone)}
# The userid to run PhotoStructure as. See <https://phstr.com/go/puid>
PUID=$(id -u) # < "id -u" will run PhotoStructure as the current user's userid.
# The groupid to run PhotoStructure as. See <https://phstr.com/go/pgid>
PGID=$(id -g) # < "id -g" will run PhotoStructure as the current user's groupid.
docker run \
--name photostructure \
--detach \
--restart unless-stopped `# < optional: see https://docs.docker.com/compose/compose-file/#restart` \
--stop-timeout 120 `# < gives PhotoStructure 2 minutes to shut down cleanly.` \
--publish 1787:1787 \
-e TZ="$TZ" \
-e PUID="$PUID" \
-e PGID="$PGID" \
-v "$PSLIBRARY":/ps/library \
-v "$PSTMP":/ps/tmp \
-v "$PSCONFIG":/ps/config \
-v "$PSLOGS":/ps/logs \
-v "$HOME"/Pictures:/pictures/ `# < CHANGE THIS LINE` \
photostructure/server:beta
Note that :alpha
, :beta
, and :stable
releases are available: read the
forum for
details.
π₯ In case of error π
If the PhotoStructure doesn’t spin up successfully, try replacing the
--detach
line with these lines:
-e PS_LOG_LEVEL=info \
-e PS_TAIL_LOGS=1
This runs PhotoStructure in --verbose
mode. Note that this can be pretty
chatty, but good for debugging why PhotoStructure is grumpy. Use
PS_LOG_LEVEL=warn
for less output, and PS_LOG_LEVEL=debug
for more output.
Still stuck? Hop onto the forum or discord and we’ll help diagnose what’s amiss.
Once you’ve fixed the issue, replace these lines back to --detach
.
Welcome to PhotoStructure! π
Open http://localhost:1787
in a browser to complete installation.
Firefox, Chrome, and Safari are supported.
While you wait for your import to complete, check out these getting-started tips!
π How to stop PhotoStructure π
Either use the navigation menu, and select “Shutdown PhotoStructure”, or run:
docker stop photostructure
(replace photostructure
with the name of your PhotoStructure docker container)
If you’re wondering why it’s taking a while to shut down, this is why.
How to restart PhotoStructure π
docker start photostructure
How to upgrade PhotoStructure π
Automatic upgrades via Watchtower (recommended) π
If you don’t mind all of your Docker containers to be upgraded automatically, use Watchtower:
docker run -d \
--name watchtower \
-v /var/run/docker.sock:/var/run/docker.sock \
containrrr/watchtower
If you only want your PhotoStructure container to be upgraded automatically,
refer to the Watchtower
documentation, and how
the watchtower
container is set up with
docker-compose.
Manual upgrade process π
Step 1: Pull the new image π
docker pull photostructure/server:stable
Step 2: Shut down the outdated container π
Select “Shutdown” from the nav menu within the PhotoStructure, or run:
docker stop -t 60 photostructure
Step 3: Rename the outdated container π
docker rename photostructure photostructure_old
Step 4: Start a new container π
docker run \
--name photostructure \
--volumes-from photostructure_old \
`# <copy and paste all the non-volume options you set in step 3, like --user, --expose and -e >`
photostructure/server
Step 5: Remove the outdated container π
docker rm photostructure_old
Uninstalling PhotoStructure π
docker rm --force photostructure
- As you see fit, remove your
LIBRARY
,CONFIG
,LOG
, andTMP
directories, but take care not to delete photos and videos you want to keep! If you want to keep the originals in your library, but remove PhotoStructure’s preview images and videos, you can delete the$LIBRARY/.photostructure/previews
directory.
ποΈ Soft-delete support π
As of v2024.4, PhotoStructure complies with the XDG trash standard.
If PhotoStructure can write to the top directory of a bind mount that you want to delete photos or videos from, PhotoStructure will handle soft-deletes automatically.
If PhotoStructure cannot create new directories to the top directory of a bind mount, you will need to do some initial setup before soft deletes on that volume will work properly.
For every bind mount you want PhotoStructure to be able to soft-delete, say, $volume
:
-
Ensure there is a
$volume/.Trash-$uid
, where$uid
is the user id you want PhotoStructure to run as, something like1000
if your docker host is Ubuntu. PhotoStructure will attempt to create this directory with correct permissions if the directory doesn’t already exist. If you prefer, you can create a$volume/.Trash
directory. The permissions on this directories should permit all users who can trash files at all to write in it, and the βsticky bitβ in the permissions must be set, if the file system supports it, so PhotoStructure can make a$volume/.Trash/$uid
directory with correct0700
permissions. -
Ensure the user that PhotoStructure is running as can write to that directory
-
Ensure the user can write to the directory that the photo or video will be soft-deleted from
Where’s my trash can contents? π
Files will only show up on a Linux desktop trashcan if the docker bind mount volume is mounted at the same depth: the XDG standard doesn’t look in mountpoint subdirectories for .Trash
or .Trash-$uid
What about ~/.local/share/Trash
? π
PhotoStructure for Docker will not attempt to use $XDG_DATA_HOME/Trash
unless the directory already exists.
This doesn’t comply with the XDG trash specification: it is a precaution against storing your soft-deleted files ephemerally in the docker container, and having a container upgrade unexpectedly hard-delete your trash can.
If you want to use this directory, bind-mount it with correct permissions into your PhotoStructure container under /home/node/.local/share/Trash
, but know that soft-deletes will now require file copies rather than file moves, which will be substantially slower depending on volume transfer speeds.
What happens if PhotoStructure cannot soft-delete a file? π
If PhotoStructure cannot move a file into a suitable .Trash
directory, PhotoStructure will hard-delete the file. A sync report will describe what action was performed for every file.
How do I disable soft-deletes? π
PhotoStructure will irreversibly delete photos and videos when you “empty trash” via the user interface if the PS_TRY_SOFT_DELETES
setting is false
.
Note that Windows and macOS don’t comply with the Freedesktop trash standard, so running docker on those platforms won’t allow you to “restore” or “empty trash”: you’ll need to find the $volume/Trash-$UID
directory and delete it manually, but it would probably be easier to just set PS_TRY_SOFT_DELETES=false
.
ποΈ Portainer instructions π
Portainer CE is an open-source docker management GUI that can be used instead of running docker
on the command-line.
Installation instructions for Linux are here, and, here’s a video walkthrough:
Thanks to forum member @avdp for recording this!
Synology docker instructions π
PhotoStructure’s docker image runs happily on intel x64 compatible Synology NAS
devices. First install the Docker package via Main Menu > Package Center
.
πΎ Install on Synology π
Unfortunately, Synology DSM’s Docker interface doesn’t support running
containers with --user
. This results in your library files being owned by
root
. It also is less secure to run PhotoStructure as root (even within a Docker container).
Because of these two issues, we recommend setting up the docker container via ssh rather than via the pointy-clicky Synology DSM interface.
Via ssh π
- Enable ssh on your NAS
- Set up the above instructions, but change
docker run
tosudo docker run
.
Via Synology DSM π
- Click
Main Menu > Docker
- Click the
Image
tab - Click
Add > Add from URL
- Enter
https://hub.docker.com/r/photostructure/server
- Pick
latest
- Wait for the image to download (it’s 300MB, it may take a minute). You’ll see a system notification when it’s done.
- Click
Launch
- Do not select high privilege. You can select resource limitation, but give PhotoStructure at least 2GB.
- Click
Advanced Settings
. - You may want to enable auto-start under the
Advanced Settings
tab. - Click the
Volume
tab. Read about the required volumes, and then add entries for/ps/library
/ps/tmp
/ps/logs
/ps/config
- and any existing directories you want to import into your library.
- Configure the network port as you’d like it. You can just map port 1787 directly.
- Click the Network tab, select “bridge”, click “Manage”, click “Add”, then select “PhotoStructure”.
- Launch the new container by clicking the power switch to the right, and wait for it to start successfully. If it fails, check under the Log tab. Email us if you need help!
Extra credit: if you click on the Container tab, double-click photostructure
,
and then click the Terminal tab, you can type --status
(and hit return) to
have PhotoStructure run health checks. (You can also run --pause
, --resume
,
and --exit
).
Upgrade on Synology π
Consider using Watchtower, which will upgrade all your docker images automatically: just ssh into your NAS, and run this:
sudo docker run -d --name watchtower -v /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower
(Click “remove container” in the DSM Docker interface to uninstall watchtower).
If you’d rather upgrade manually, it’s a simple 16 step process:
- Click
Main Menu > Docker
- Click the
Container
tab - Click your PhotoStructure container to select it
- Click
Settings > Export
- Select
Export container settings
(you don’t need to store the container contents). - The export should be a small JSON file. Store it locally or on your NAS.
- With your PhotoStructure container still selected, make sure it isn’t running. If it is, select
Action > Stop
. - Click
Action > Delete
. (this is scary, but as long as you correctly mounted/ps/library
, your library will be on your NAS, not inside this container). - Click the
Image
tab - Click
Add > Add from URL
- Enter
https://hub.docker.com/r/photostructure/server
- Pick
latest
- Wait for the image to download (it’s 300MB, it may take a minute). You’ll see a system notification when it’s done.
- Return to the Container tab, and click
Settings > Import
- Import the JSON file you exported in step 6.
- Launch the new container by clicking the power switch to the right
What about Docker Volumes? π
Docker supports volumes, and “are the preferred mechanism for persisting data generated by and used by Docker containers”.
I recommend using bind mounts because the resulting library and internal files are portable to other editions of PhotoStructure on any operating system. You’re certainly free to use Docker Volumes instead of bind mounts, especially if you know for certain you’ll never want to move your library to another computer, or switch to a different container host (like Podman). You may see slightly better I/O-bound performance (like, with the library database and preview images) if you use a Docker Volume, but an SSD is recommended for the library db, previews, and scratch directory, either way.
Podman support π
Forum user iamphotocat submitted podman installation instructions–thanks a bunch!
Edit history π
April 25, 2024 π
- Soft-delete instructions
November 8, 2023 π
- Integrated Docker easy-mode instructions
- Added use-cases
July 13, 2022 π
- Added new Portainer section
- Deleted QNAP instructions, which is not supported.
January 3 3, 2022 π
- Updated the shell script to include a shebang and more comments around
TZ
,PUID
, andPGID
.
August 22, 2021 π
-
Make all image names use
photostructure/server:info
-
Add
container_name
to QNAP’s docker-compose.yml
August 12, 2021 π
- Replace
--user
references with v1.0’sPUID
/PGID
November 17, 2020 π
- Added QNAP instructions
September 22, 2020 π
- Switched to
--restart on-failure
- Added
--stop-timeout 120
August 7, 2020 π
- Set up
--user
instructions - Normal users don’t have read/write to docker volumes, so
/ps/log
and/ps/cache
defaults are bind mounts in the example now.
August 4, 2020 π
- Added more detail about the different volumes
- Removed
--init
from thedocker run
command, as v0.8.3 doesn’t require it anymore - Added section for docker on Synology
- Pulled docker-compose instructions into new doc
July 31, 2020 π
- Systemd start-on-boot configurations were replaced with docker’s built-in
restart: unless-stopped
functionality. - Added config fix for https reverse proxy issues with Server-side events.
- Clarified when
.uuid
files were needed (only for imported volumes, not the library itself).
July 20, 2020 π
- If you used versions prior to 0.8, these instructions have changed. We simplified the instructions to be vanilla Docker,although you’re free to use docker-compose if you prefer.