This project is called "Simple Private Image Server" or SPIS for short. It's purpose is to be a lightweight and fast server to display media hosted on a private server. This project came about when I was searching for a solution like this and found nothing. Everything seemed way too feature heavy and slow, requiring you to setup databases and other unnecessary components.
The goals for this project are:
- Simple to setup 🏝️
- Flexible to operate ➰
- Lightweight, multi-threaded and fast 🚀
- Minimalistic GUI 🤩
- Easy to use on mobile 📱
Some features worth mentioning:
- Endless scrolling 📜
- Mark favorites ❤️
- Filter by year, month, favorites 🎚️
- Instantly load new files 📨
- Is a progressive web app 📲
I personally use this project to host around 40.000 images on a Raspberry Pi CM4 🤯
If this project is just what you needed and/or has been helpful to you, please consider buying me a coffee ☕
This is how the GUI looks on mobile!
Everything is configured via environmental variables:
| Variable Name | Required | Default | Description |
|---|---|---|---|
SPIS_MEDIA_DIR |
Yes |
What directory SPIS will look for media |
|
SPIS_DATA_DIR |
Yes |
What directory SPIS will store its data (database and thumbnails) |
|
SPIS_PROCESSING_SCHEDULE |
No |
0 0 2 * * * |
When should SPIS scan for new media (default is every night at 2) |
SPIS_PROCESSING_RUN_ON_START |
No |
false |
Should SPIS scan for media on startup |
SPIS_API_MEDIA_PATH |
No |
/assets/media |
On what path will the webserver (nginx) serve media files * |
SPIS_API_THUMBNAIL_PATH |
No |
/assets/thumbnails |
On what path will the webserver (nginx) serve thumbnails * |
SPIS_SERVER_SOCKET |
No |
/var/run/spis.sock |
Path of the socket SPIS will listen to ** |
SPIS_SERVER_ADDRESS |
No |
Address SPIS will listen to rather than socket, i.e. 0.0.0.0:8000 ** |
|
RUST_LOG |
No |
Loglevels of the application, i.e. error,spis_server=info |
* These are the paths that the webserver (nginx) serves media and thumbnails on. For a details on how this works, look at the diagram.
** You cannot make SPIS listen to both a socket and an address. So by setting SPIS_SERVER_ADDRESS you will disable SPIS_SERVER_SOCKET.
Easiest way to run SPIS is with the docker image:
$ docker run -it \
-p 8080:8080 \
-v /path/to/your/media:/var/lib/spis/media \
-v /path/to/save/data:/var/lib/spis/data \
ghcr.io/gbbirkisson/spisor using docker compose:
services:
spis:
image: ghcr.io/gbbirkisson/spis
ports:
- "8080:8080"
volumes:
# This assumes you want to keep the SPIS data in a
# docker volume and not in some directory
- data:/var/lib/spis/data
- ./path/to/your/media:/var/lib/spis/media
volumes:
data:Tip
Try running the docker compose example by running...
$ cd examples/docker
$ docker compose up... and open up http://localhost:8080 in your browser.
If you want to run the binary, you will need to understand that SPIS needs a webserver to serve media.
Because, serving images and videos is complicated! It involves caching, compressing, streaming and a host of other problems that SPIS does not need to know about. Some people that are way smarter than me have found a solution for all these problems. So instead of implementing a bad solution in SPIS, we stand on the shoulders of others and use a tried and tested webserver to handle this complexity for us.
So how do these things tie together. Well here is a simplified diagram of what happens when you open up SPIS in the browser.
Note
Never during the interaction does SPIS read images of the file system and serve them.
sequenceDiagram
participant U as Browser
participant W as Webserver
participant S as SPIS
participant F as File System
autonumber
Note over U: User opens webpage in browser
U ->> W: GET /
W ->> S: GET /
S ->> W: Returns page
W ->> U: Returns page
Note over U: Browser fetches thumbnails
U ->> W: GET <SPIS_API_THUMBNAIL_PATH>/thumb.webp<br/>i.e.<br/>GET /assets/thumbnails/thumb.webp
W -->> F: Reads <SPIS_DATA_DIR>/thumbnails/thumb.webp<br/>i.e.<br/>Reads /data/thumbnails/thumb.webp
W ->> U: Returns thumb.webp
Note over U: Browser fetches video
U ->> W: GET <SPIS_API_MEDIA_PATH>/video.mp4<br/>i.e.<br/>GET /assets/media/video.mp4
W -->> F: Reads <SPIS_MEDIA_DIR>/video.mp4<br/>i.e.<br/>Reads /media/video.mp4
W ->> U: Streams video.mp4
Well these are the steps:
- Download a binary for your architecture and put in your path
- Install a webserver
- For video support make sure
ffmpegandffprobeare in your path. - Configure
SPISand run....we will get back to this - Configure webserver and run....we will get back to this
Now, steps 4-5 are super unhelpful (a bit like instructions on how to draw an owl). This is because SPIS is flexible, and does not care how you do this. You can use any combination of webserver + supervisor to get this up and running. So covering every single way to set this up is not feasible.
So I'm just going to describe how to do this with systemd and nginx on a debian system.
- Download a binary for your architecture and save it as
/usr/local/bin/spis. Make sure it is executable (chmod +x /usr/local/bin/spis) - Install
nginx:sudo apt install nginx - Add video support:
apt install ffmpeg - Configure
SPISand run:- Create these folders and make sure user
www-dataowns them:/storage/spis/data/storage/spis/media
- Create the file
/etc/systemd/system/spis.service - Set the contents of the file to be the same as the spis.service example.
- Enable and start
SPIS:systemctl enable --now spis
- Create these folders and make sure user
- Configure
nginxand run:- Create the file
/etc/nginx/sites-available/default - Set the contents of the file to be the same as the nginx.conf example.
- Enable and start
nginx:systemctl enable --now nginx
- Create the file
Now SPIS will process and serve any image/video that you place in /storage/spis/media. Just make sure the files are owned by the www-data user.
Open up SPIS on http://yourserver:8080
You can add SPIS as a PWA to your desktop or mobile. Open up the SPIS home page in a browser on the device, open the top-right menu, and select Add to Home screen, Install or something to that extent.
This project uses release-please and because of how it is set up, there are separate release notes for each component of SPIS. Therefore the easiest way to read all the release notes in one place is to look at the relevant PR for each release. I know its a bit tedious to find the right PR, sorry about that.
$ make setup$ pre-commit install --hook-type commit-msgI leave it up do you to put some images/videos in the ./dev/api/media folder.
Run stack with:
$ make devOr alternatively open 3 terminals and run:
$ make dev-nginx$ make dev-server$ make dev-guiAnd then open http://localhost:7000 in your browser
Nginx uses basic auth with credentials in /etc/nginx/.htpasswd