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/spis
or 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
ffmpeg
andffprobe
are in your path. - Configure
SPIS
and 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
SPIS
and run:- Create these folders and make sure user
www-data
owns 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
nginx
and 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-msg
I leave it up do you to put some images/videos in the ./dev/api/media
folder.
Run stack with:
$ make dev
Or alternatively open 3 terminals and run:
$ make dev-nginx
$ make dev-server
$ make dev-gui
And then open http://localhost:7000 in your browser
Nginx uses basic auth with credentials in /etc/nginx/.htpasswd