bestia.dev This is a copy of the Codeberg readme, only because of SEO. Find the original on https://codeberg.org/bestia-dev-work-in-progress/web_photo_folders_server_1

web_photo_folders_server_1

Simple web photo gallery with folder tree.
version: 2026.901.927 date: 2026-09-01 author: bestia.dev repository: codeberg.org

work-in-progress tutorial rust

License web_photo_folders_server_1

Hashtags: #tutorial #rust
My projects on codeberg.org are more like a tutorial than a finished product: bestia-dev tutorials.

Goals

Make this project in small increments in separate repositories to make the progress visible to beginners.
I want to display my photos on the web, but just for me.
It has to be secured by google oauth2.
My photos are in a neat folder structure.
I have a lot of photos and videos.
The web page has the folder tree on the left and thumbnails on the right with movable divider.
Click on the photo to see it as big as possible.
Use key presses to navigate. Mouse use is secondary, but it must be there.
Always use the server inside a linux container with podman.
The server is build using musl, so the container can be very small.

Axum web server

I will use the Axum web server crate.
Axum uses some type magic flexibility in function signatures called Extractors.
Rust doesn't have function overloading.
Magical handler functions in Rust
dependency injection like Bevy
extract-as-argument pattern
The internal port for Axum web-app will be 8080. Then in podman run it will be forwarded as 8012 or other port.

WSL

I am using WSL inside Win11.
By default the port forwarding between WSL and Windows is limited I guess.
Modify the file c:\Users\Luciano\.wslconfig to contain:

[wsl2]
# forward ports from WSL to Win, so I can have a web server on port 8012 in WSL and use the browser in Windows
localhostForwarding = true

Then restart the computer or only the WSL so the change can take effect.
Sometimes WSL just don't forward the ports with no obvious reason. Just restart WSL and it will work again.

Linux container

I will run the web server inside a new container in WSL, not inside the CRUSTDE container.
Follow the instructions at the step cargo auto release to copy the new executable binary, create a container image and run it in WSL.
Open the browser in Windows and it should work.
How to Create a Container from Scratch with Buildah and Podman
There will be 2 volumes/folders attached to the web_photo_folders_cnt_8012 container: photos_readonly and thumbnails. \

Install to web server

Edit the reverse proxy nginx file /etc/nginx/sites-available/default

# in remote web server bash
sudo fresh /etc/nginx/sites-available/default
# add these lines to the nginx config

    # region web_photo_folders_8012
        # only complete route with trailing slash works
        # the trailing / after both of these lines means this route is not appended to the forwarding
        location /web_photo_folders_8012/ {
            proxy_pass http://127.0.0.1:8012/web_photo_folders/;
            proxy_buffering off;
        }
    # endregion web_photo_folders_8012

    # region web_photo_folders_8013
        # only complete route with trailing slash works
        # the trailing / after both of these lines means this route is not appended to the forwarding
        location /web_photo_folders_8013/ {
            proxy_pass http://127.0.0.1:8013/web_photo_folders/;
            proxy_buffering off;
        }
    # endregion web_photo_folders_8013

# after save, reload nginx
sudo service nginx reload

Install the folders and container on the web server.

# in WSL bash terminal
sshadd_emb hetzner_com_debian_1_luciano_ssh_1
# Copy the example photos: first `dry-run`, then without `dry-run`
rsync -e ssh -avz --itemize-changes --delete-after --progress /home/luciano/rustprojects/web_photo_folders_8012/ hetzner_com_debian_1_luciano:/var/www/webapps/web_photo_folders_8012/
rsync -e ssh -avz --itemize-changes --delete-after --progress /home/luciano/rustprojects/web_photo_folders_8013/ hetzner_com_debian_1_luciano:/var/www/webapps/web_photo_folders_8013/

sshadd_emb hetzner_com_debian_1_luciano_ssh_1
ssh luciano@bestia.dev
# in the remote bash
zellij attach
# new tab: ctrl+t (new tab), n (new)
podman stop --time 1 web_photo_folders_cnt_8012
podman stop --time 1 web_photo_folders_cnt_8013
podman rm -f web_photo_folders_cnt_8012
podman rm -f web_photo_folders_cnt_8013

podman pull docker.io/bestiadev/web_photo_folders_img:v1.0.0

# In `podman run` I can customize the port and the volumes .
# In nginx reverse proxy I can use a different `location` for customized web-apps.
# The permissions of rootless podman container and the host folders can be complicated.
# To make it simple add `--userns=keep-id`, so the same user that runs the podman command 
# is also the use that access the host folders.
podman run -d  \
--name web_photo_folders_cnt_8012  \
--userns=keep-id  \
-p 127.0.0.1:8012:8080   \
--env WEB_PHOTO_FOLDERS_SERVER_LOG="debug,hyper_util=error,reqwest=error,rustls=error,h2=error"  \
--env WEB_APP_DIRECTORY="web_photo_folders_cnt_8012"  \
-v /var/www/webapps/web_photo_folders_8012/photos_readonly:/web_server_folder/photos_readonly:ro  \
-v /var/www/webapps/web_photo_folders_8012/thumbnails:/web_server_folder/thumbnails  \
-v /var/www/webapps/web_photo_folders_8012/logs:/logs  \
docker.io/bestiadev/web_photo_folders_img:v1.0.0

podman logs web_photo_folders_cnt_8012

#region: logs not working
    # If the original podman logs command is not working,
    # you need to add the user to the systemd-journal group
    getent passwd
    getent group
    lsattr /etc/passwd
    $ ----i---------e------- /etc/passwd   ( i stands for immutable)
    # disable immutable
    sudo chattr -i /etc/passwd

    whoami
    # add user luciano to group systemd-journal
    sudo usermod -a -G systemd-journal luciano
    # User must refresh their group membership: Log out and log back in

    # again enable immutable
    sudo chattr +i /etc/passwd

    # The server also writes tracing to a log
    ls -la  /var/www/webapps/web_photo_folders_8012/logs
    tail /var/www/webapps/web_photo_folders_8012/logs/web_photo_folders_server.2026-08-31.log

    # check used ports
    ss -lntu
    netstat -ntlp
#endregion: logs not working

curl http://127.0.0.1:8012/
curl http://127.0.0.1:8012/web_photo_folders
curl http://127.0.0.1:8012/web_photo_folders/thumbnails/index.html
curl http://127.0.0.1:8012/web_photo_folders/photos_readonly/index.html
curl http://127.0.0.1:8012/web_photo_folders/photos_readonly/LF2026-02-26%2011-34-27%20gigi%20winny.jpg

podman run -d  \
--name web_photo_folders_cnt_8013  \
--userns=keep-id  \
-p 127.0.0.1:8013:8080   \
--env WEB_PHOTO_FOLDERS_SERVER_LOG="debug,hyper_util=error,reqwest=error,rustls=error,h2=error"  \
--env WEB_APP_DIRECTORY="web_photo_folders_cnt_8013"  \
-v /var/www/webapps/web_photo_folders_8013/photos_readonly:/web_server_folder/photos_readonly:ro  \
-v /var/www/webapps/web_photo_folders_8013/thumbnails:/web_server_folder/thumbnails  \
-v /var/www/webapps/web_photo_folders_8013/logs:/logs  \
docker.io/bestiadev/web_photo_folders_img:v1.0.0

podman logs web_photo_folders_cnt_8013

curl http://127.0.0.1:8013/web_photo_folders/photos_readonly/index.html
curl http://127.0.0.1:8013/web_photo_folders/photos_readonly/2026/LF2026-03-24%2011-21-50%20gigi%20nice.jpg

Try in any browser:
https://bestia.dev/web_photo_folders_8012/
http://bestia.dev/web_photo_folders_8012/photos_readonly/LF2026-02-26%2011-34-27%20gigi%20winny.jpg

https://bestia.dev/web_photo_folders_8013/
http://bestia.dev/web_photo_folders_8013/photos_readonly/2026/LF2026-03-24%2011-21-50%20gigi%20nice.jpg

Development details

Read the development details in a separate md file: DEVELOPMENT.md

Releases changelog

Read the releases changelog in a separate md file: RELEASES.md

TODO

Everything I want for the version 1 of the 'tutorial' is done.
Development will continue in version 2.

Open-source and free as a beer

My open-source projects are free as a beer (MIT license).
I just love programming.
But I need also to drink. If you find my projects and tutorials helpful, please buy me a beer by donating to my PayPal.
You know the price of a beer in your local bar ;-)
So I can drink a free beer for your health :-)
Na zdravje! Alla salute! Prost! Nazdravlje! 🍻

//bestia.dev
//codeberg.org/bestia-dev
//github.com/bestia-dev
//bestiadev.substack.com
//youtube.com/@bestia-dev-tutorials