From 0193f7703d333fc42ef63392e6bce32535575dec Mon Sep 17 00:00:00 2001
From: Kostiantyn <32730812+kkovaletp@users.noreply.github.com>
Date: Wed, 15 May 2024 11:58:02 +0300
Subject: [PATCH] Refactor docker deployment and user setup (#863)

* Fix #862, address #826 and maybe some other tickets: reimplemented the docker-compose user setup to enhance product setup experience and cover much more cases.

* make unique DB container name and use it in communication from Photoview

* Removed unnecessary healthcheck for photoview from docker-compose.example.yml, as it is defined in the Dockerfile; optimized Dockerfile combining all RUN directives of PROD stage into 1, which will produce single layer and save some space; added Dockerfile-dev, docker-compose-dev.yml, and new "dev" and "dev-down" directives into Makefile, which allows to setup development env in Docker. Instructions of how to use it are in comments at the beginning of Dockerfile-dev and docker-compose-dev.yml files

* Set RWX permissions to the application's working folder for any user, so that the image could be later run with non-root permissions and the app still be able to do needed operations in the FS

* Enhanced the "Getting started" section in the readme; added the `help` target and enhanced comments in the Makefile; commented out the `docker system prune -f` with the comment about the command and why it is there; added optional and commented by default `7zz` commands to the `backup` section of the Makefile

* Use `slim` base image for final photoview image

* Implement SQLite support according to the PR #851

* Removed deprecated `version` line from compose files; optimized dockerfile to build with less layers and run as non-root; mapped only Photoview related services to Watchtower by default instead of updating all running images on a host; added template for Postgres to the .env; reverted compose executable definition, so the new compose is called when present; added a tip about `lnav` to help

* fix a typo in the username; add support of PostgreSQL; split and optimize backup target in Makefile

* Fixed some typos and styling in Readme, excluded dev-environment setup from the PR; added a list of tips on how to secure Photoview in the Advanced setup section of Readme

* Implemented many security improvements, suggested by @Omar007, switched to the dedicated Darktable's repo to install the latest released version, as asked in #935; switched Watchtower to labels instead of profiles

* forgot the compose file

* move face models back to /app folder; comment out and document unnecessary vars in compose; fix a typo in a few vars

* Exclude Makefile in the root folder from git; documented multiple mounts case better; fixed incorrect SQLite DB path

* Fixed several bugs after complete testing cycle with all 3 DBs

* removed hardcoded port in Dockerfile

* Pin the major version for the `photoview` image for stability

* Revert back to the port 80 inside the container on product owner's request

* Provide a minimal compose file and update the readme accordingly

* Handle incorrect media file and folder permissions; set correct permissions for storage folder; fix healthcheck command for postgres

---------

Co-authored-by: Konstantin Koval <kkb@ukr.net>
---
 .dockerignore                                 |  10 ++
 .gitignore                                    |   3 +
 Dockerfile                                    | 101 +++++------
 README.md                                     |  92 +++++++---
 docker-compose example/Makefile               | 116 ++++++++++++
 .../docker-compose.example.yml                | 166 ++++++++++++++++++
 .../docker-compose.minimal.example.yml        |  82 +++++++++
 docker-compose example/example.env            |  62 +++++++
 docker-compose.example.yml                    |  58 ------
 9 files changed, 556 insertions(+), 134 deletions(-)
 create mode 100644 docker-compose example/Makefile
 create mode 100644 docker-compose example/docker-compose.example.yml
 create mode 100644 docker-compose example/docker-compose.minimal.example.yml
 create mode 100644 docker-compose example/example.env
 delete mode 100644 docker-compose.example.yml

diff --git a/.dockerignore b/.dockerignore
index d317fb8..695c95f 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,12 +1,22 @@
+.dockerignore
 .git
+.github
 .gitignore
+.husky
 .prettierrc
 .eslintcache
 .vscode
 .env
+docker-compose example
+docker-compose.yml
+docker-compose-dev.yml
+Dockerfile
+Dockerfile-dev
 example.env
+Makefile
 photos_path
 screenshots
+storage
 
 ui/node_modules/
 ui/build/
diff --git a/.gitignore b/.gitignore
index fc0762c..9418adc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,9 @@ testing.env
 
 # docker
 docker-compose.yml
+/Makefile
+storage
+database
 
 # dependencies
 node_modules/
diff --git a/Dockerfile b/Dockerfile
index 5d3435c..4929d5c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,84 +20,85 @@ ARG COMMIT_SHA
 ENV COMMIT_SHA=${COMMIT_SHA:-}
 ENV REACT_APP_BUILD_COMMIT_SHA=${COMMIT_SHA:-}
 
-RUN mkdir -p /app
-WORKDIR /app
-
 # Download dependencies
-COPY ui/package*.json /app/
-RUN npm ci --omit=dev --ignore-scripts
-
-# Build frontend
 COPY ui /app
-RUN npm run build -- --base=$UI_PUBLIC_URL
+WORKDIR /app
+RUN npm ci --omit=dev --ignore-scripts \
+  # Build frontend
+  && npm run build -- --base=$UI_PUBLIC_URL
 
 ### Build API ###
 FROM --platform=${BUILDPLATFORM:-linux/amd64} debian:bookworm AS api
 ARG TARGETPLATFORM
 
 COPY docker/install_build_dependencies.sh /tmp/
-RUN chmod +x /tmp/install_build_dependencies.sh && /tmp/install_build_dependencies.sh
-
 COPY docker/go_wrapper.sh /go/bin/go
-RUN chmod +x /go/bin/go
+COPY api /app
+WORKDIR /app
+
 ENV GOPATH="/go"
 ENV PATH="${GOPATH}/bin:${PATH}"
-
 ENV CGO_ENABLED 1
 
-RUN go env
-
-RUN mkdir -p /app
-WORKDIR /app
-
 # Download dependencies
-COPY api/go.mod api/go.sum /app/
-RUN go mod download
-
-# Patch go-face
-RUN sed -i 's/-march=native//g' ${GOPATH}/pkg/mod/github.com/!kagami/go-face*/face.go
-
-# Build dependencies that use CGO
-RUN go install \
-  github.com/mattn/go-sqlite3 \
-  github.com/Kagami/go-face
-
-# Copy and build api source
-COPY api /app
-RUN go build -v -o photoview .
+RUN chmod +x /tmp/install_build_dependencies.sh \
+  && chmod +x /go/bin/go \
+  && /tmp/install_build_dependencies.sh \
+  && go env \
+  && go mod download \
+  # Patch go-face
+  && sed -i 's/-march=native//g' ${GOPATH}/pkg/mod/github.com/!kagami/go-face*/face.go \
+  # Build dependencies that use CGO
+  && go install \
+    github.com/mattn/go-sqlite3 \
+    github.com/Kagami/go-face \
+  # Build api source
+  && go build -v -o photoview .
 
 ### Copy api and ui to production environment ###
-FROM debian:bookworm
+FROM debian:bookworm-slim
 ARG TARGETPLATFORM
-WORKDIR /app
 
-COPY api/data /app/data
-
-RUN apt update \
+# Create a user to run Photoview server
+RUN useradd -r -U -m photoview \
   # Required dependencies
-  && apt install -y curl gpg libdlib19.1 ffmpeg exiftool libheif1
-
-# Install Darktable if building for a supported architecture
-RUN if [ "${TARGETPLATFORM}" = "linux/amd64" ] || [ "${TARGETPLATFORM}" = "linux/arm64" ]; then \
-  apt install -y darktable; fi
-
-# Remove build dependencies and cleanup
-RUN apt purge -y gpg \
-  && apt autoremove -y \
-  && apt clean \
+  && apt-get update \
+  && apt-get install -y curl gnupg gpg libdlib19.1 ffmpeg exiftool libheif1 sqlite3 \
+  # Install Darktable if building for a supported architecture
+  && if [ "${TARGETPLATFORM}" = "linux/amd64" ] || [ "${TARGETPLATFORM}" = "linux/arm64" ]; then \
+    echo 'deb https://download.opensuse.org/repositories/graphics:/darktable/Debian_12/ /' \
+      | tee /etc/apt/sources.list.d/graphics:darktable.list; \
+    curl -fsSL https://download.opensuse.org/repositories/graphics:/darktable/Debian_12/Release.key \
+      | gpg --dearmor | tee /etc/apt/trusted.gpg.d/graphics_darktable.gpg > /dev/null; \
+    apt-get update; \
+    apt-get install -y darktable; \
+  fi \
+  # Remove build dependencies and cleanup
+  && apt-get purge -y gnupg gpg \
+  && apt-get autoremove -y \
+  && apt-get clean \
   && rm -rf /var/lib/apt/lists/*
 
-COPY --from=ui /app/dist /ui
+WORKDIR /home/photoview
+COPY api/data /app/data
+COPY --from=ui /app/dist /app/ui
 COPY --from=api /app/photoview /app/photoview
 
 ENV PHOTOVIEW_LISTEN_IP 127.0.0.1
 ENV PHOTOVIEW_LISTEN_PORT 80
 
 ENV PHOTOVIEW_SERVE_UI 1
-ENV PHOTOVIEW_UI_PATH /ui
+ENV PHOTOVIEW_UI_PATH /app/ui
+ENV PHOTOVIEW_FACE_RECOGNITION_MODELS_PATH /app/data/models
+ENV PHOTOVIEW_MEDIA_CACHE /home/photoview/media-cache
 
-EXPOSE 80
+EXPOSE ${PHOTOVIEW_LISTEN_PORT}
 
-HEALTHCHECK --interval=60s --timeout=10s CMD curl --fail 'http://localhost:80/api/graphql' -X POST -H 'Content-Type: application/json' --data-raw '{"operationName":"CheckInitialSetup","variables":{},"query":"query CheckInitialSetup { siteInfo { initialSetup }}"}'
+HEALTHCHECK --interval=60s --timeout=10s \
+  CMD curl --fail http://localhost:${PHOTOVIEW_LISTEN_PORT}/api/graphql \
+    -X POST -H 'Content-Type: application/json' \
+    --data-raw '{"operationName":"CheckInitialSetup","variables":{},"query":"query CheckInitialSetup { siteInfo { initialSetup }}"}' \
+    || exit 1
 
+USER photoview
 ENTRYPOINT ["/app/photoview"]
diff --git a/README.md b/README.md
index 8965620..7b40dc5 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 <img src="./screenshots/photoview-logo.svg" height="92px" alt="photoview logo" />
 
-[![License](https://img.shields.io/github/license/viktorstrate/photoview)](./LICENSE.md)
+[![License](https://img.shields.io/github/license/viktorstrate/photoview)](./LICENSE.txt)
 [![GitHub contributors](https://img.shields.io/github/contributors/viktorstrate/photoview)](https://github.com/viktorstrate/photoview/graphs/contributors)
 [![Docker Pulls](https://img.shields.io/docker/pulls/viktorstrate/photoview)](https://hub.docker.com/r/viktorstrate/photoview)
 [![Docker builds](https://github.com/photoview/photoview/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/photoview/photoview/actions/workflows/build.yml)
@@ -10,9 +10,9 @@
 
 Photoview is a simple and user-friendly photo gallery that's made for photographers and aims to provide an easy and fast way to navigate directories, with thousands of high-resolution photos.
 
-You configure Photoview to look for photos and videos within a directory on your file system. The scanner automatically picks up your media and start to generate thumbnail images to make browsing super fast.
+You configure Photoview to look for photos and videos within a directory on your file system. The scanner automatically picks up your media and starts to generate thumbnail images to make browsing super fast.
 
-When your media has been scanned they show up on the website, organised in the same way as on the filesystem.
+When your media has been scanned, they show up on the website, organised in the same way as on the filesystem.
 
 > If you have questions regarding setup or development,
 feel free to join the Discord server https://discord.gg/jQ392948u9
@@ -30,19 +30,20 @@ Password: **demo**
 - [Main features](#main-features)
 - [Supported Platforms](#supported-platforms)
 - [Why yet another self-hosted photo gallery](#why-yet-another-self-hosted-photo-gallery)
-- [Getting started - Setup with Docker](#getting-started---setup-with-docker)
-- [Set up development environment](#setup-development-environment)
+- [Getting started — Setup with Docker](#getting-started--setup-with-docker)
+- [Advanced setup](#advanced-setup)
+- [Set up development environment](#set-up-development-environment)
 - [Sponsors](#sponsors)
 
 ## Main features
 
-- **Closely tied to the file system**. The website presents the images found on the local filesystem of the server, directories are mapped to albums.
+- **Closely tied to the file system**. The website presents the images found on the local filesystem of the server; directories are mapped to albums.
 - **User management**. Each user is created along with a path on the local filesystem, photos within that path can be accessed by that user.
 - **Sharing**. Albums, as well as individual media, can easily be shared with a public link, the link can optionally be password protected.
 - **Made for photography**. Photoview is built with photographers in mind, and thus supports **RAW** file formats, and **EXIF** parsing.
 - **Video support**. Many common video formats are supported. Videos will automatically be optimized for web.
 - **Face recognition**. Faces will automatically be detected in photos, and photos of the same person will be grouped together.
-- **Performant**. Thumbnails are automatically generated and photos first load when they are visible on the screen. In full screen, thumbnails are displayed until the high resolution image has been fully loaded.
+- **Performant**. Thumbnails are automatically generated and photos first load when they are visible on the screen. In full screen, thumbnails are displayed until the high-resolution image has been fully loaded.
 - **Secure**. All media resources are protected with a cookie-token, all passwords are properly hashed, and the API uses a strict [CORS policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
 
 ## Supported platforms
@@ -64,53 +65,92 @@ There exists a lot of open-source self-hosted photo galleries already. Here are
 
 So why another one?
 I love taking photos, and I store all of them on my local fileserver.
-This is great because I can organize my photos directly on the filesystem so it's easy to move them or take backups. I want to be able to control where and how the photos are stored.
+This is great because I can organize my photos directly on the filesystem, so it's easy to move them or take backups. I want to be able to control where and how the photos are stored.
 
-The problem is however that RAW images are extremely tedious to navigate from a fileserver, even over the local network.
+The problem is, however, that RAW images are extremely tedious to navigate from a fileserver, even over the local network.
 
-My server holds a lot of old family pictures, that I would like my family to have access to as well.
+My server holds a lot of old family pictures that I would like my family to have access to as well.
 And some of the pictures I would like to easily be able to share with other people without the hassle of them having to make an account first.
 
-Thus I need a solution that can do the following:
+Thus, I need a solution that can do the following:
 
-- A scan based approach that automatically organises my photos
+- A scan-based approach that automatically organises my photos
 - Support RAW and EXIF parsing
 - Have support for multiple users and ways to share albums and photos also publicly
-- Be simple and fast to use
+- Be straightforward and fast to use
 
-All of the photo galleries can do a lot of what I need, but no single one can do it all.
+All the photo galleries can do a lot of what I need, but no single one can do it all.
 
-## Getting started - Setup with Docker
+## Getting started — Setup with Docker
 
 > This section describes how to get Photoview up and running on your server with Docker.
-> Make sure you have Docker and docker-compose installed and running on your server
+> Make sure you have Docker and docker-compose installed and running on your server.
+> `make` should be installed as well if you'd like to use provided `Makefile`, which is optional (see step 4 for more details).
+> `7zz` should be installed in case, you'd like to use it in scope of the backup scenario instead of the default .tar.xz format. Read the comment in the `Makefile`, located on top of the `backup` section for more details.
 
-1. Make a new `docker-compose.yml` file on your computer, and copy the content of [docker-compose.example.yml](/docker-compose.example.yml) to the new file.
-2. Edit `docker-compose.yml`, find the comments starting with `Change This:`, and change the values, to properly match your setup. If you are just testing locally, you don't have to change anything.
-3. Start the server by running the following command
+1. Download the content of the `docker-compose example` folder to the folder on your server, where you expect to host the Photoview internal data (database and cache files).
+   
+   Please note that this folder contains 2 versions of the docker-compose file:
+   - `docker-compose.example.yml` - the fully-functional and recommended for the most cases config
+   - `docker-compose.minimal.example.yml` - the minimal and simple config for those, who find the previous one too complex and difficult to understand and manage
+   
+   When downloading files, you need to choose only one of them.
+2. Rename downloaded files and remove the `example` from their names (so, you need to have `.env`, `docker-compose.yml`, and `Makefile` files). If you choose the `docker-compose.minimal.example.yml` on previous step, make sure to rename it to the `docker-compose.yml`.
+3. Open these files in a text editor and read them. Modify where needed according to the documentation comments to properly match your setup. There are comments of 2 types: those, starting with `##`, are explanations and examples, which should not be uncommented; those, starting with `#`, are optional or alternative configuration parts, which might be uncommented in certain circumstances, described in corresponding explanations. It is better to go through the files in the next order: `.env`, `docker-compose.yml`, and `Makefile`.
+4. Make sure that your media library's root folder and all the files and subfolders are readable and searchable by other users: run the next command (or corresponding sequence of commands from the `Makefile`):
 
-```bash
-$ docker-compose up -d
-```
+   ```bash
+   make readable
+   ```
+
+   If command(s) return `Permission denied` error, run them under the user, owning corresponding files and folders. Alternatively, run them adding `sudo ` before the command: this will switch the execution context to `root` user and ask for the root password. You have to have permission to run `sudo` in the system.
+
+   If you configured other mounts with media files from other locations on the host (like HOST_PHOTOVIEW_MEDIA_FAMILY or anything else), you need to run the same commands, as in the `Makefile` `readable` target, for each media root folder on your host manually: copy each command to your shell and replace the variable with the absolute path to an additional media root folder without the trailing `/`. Run both commands for each additional root folder.
+5. In case, you don't have `make` installed in your system or don't want to use it for the Photoview management activities, you could use the same commands from the `Makefile` and run them in your shell directly, or create your own scripts. Make sure to apply or replace the variables from your `.env` first in this case. `Makefile` is provided just for your convenience and simplicity, but is optional.
+6. Start the server by running the following command (or corresponding sequence of commands from the `Makefile`):
+
+   ```bash
+   make all
+   ```
 
 If the endpoint or the port hasn't been changed in the `docker-compose.yml` file, Photoview can now be accessed at http://localhost:8000
 
 ### Initial Setup
 
-If everything is setup correctly, you should be presented with an initial setup wizard, when accessing the website the first time.
+If everything is set up correctly, you should be presented with an initial setup wizard when accessing the website the first time.
 
 ![Initial setup](./screenshots/initial-setup.png)
 
 Enter a new username and password.
 
-For the photo path, enter the path in the docker container where your photos are located.
-This can be set from the `docker-compose.yml` file under `api` -> `volumes`.
-The default location is `/photos`
+For the photo path, enter the path inside the docker container where your photos are located.
+This can be set from the `docker-compose.yml` file under `photoview` -> `volumes`.
+The default location is `/photos`.
 
 A new admin user will be created, with access to the photos located at the path provided under the initial setup.
 
 The photos will have to be scanned before they show up, you can start a scan manually, by navigating to `Settings` and clicking on `Scan All`
 
+## Advanced setup
+
+We suggest securing the Photoview instance before exposing it outside your local network: even while it provides read-only access to your media gallery and has basic user authentication functionality, it is not enough to protect your private media from malicious actors on the Internet.
+
+Possible ways of securing a self-hosted service might be (but not limited to):
+
+1. Configure a **Firewall** on your local network's gateway and allow only the intended type of incoming traffic to pass.
+2. Use **VPN** to provide external access to local services.
+3. Setting up a **Reverse proxy** in front of the service and forwarding all the traffic through it, exposing HTTPS port with strong certificate and cipher suites to the Internet. This could be one of the next products or something else that you prefer:
+   - [Traefic Proxy](https://doc.traefik.io/traefik/)
+   - [NGinx Proxy Manager](https://nginxproxymanager.com/guide/)
+   - [Cloudflare Gateway](https://www.cloudflare.com/zero-trust/products/gateway/)
+4. Configure an external **Multi-Factor Authentication** service to manage authentication for your service (part of Cloudflare services, but you can choose anything else).
+5. Configure **Web Application Firewall** to protect from common web exploits like SQL injection, cross-site scripting, and cross-site forgery requests (part of Cloudflare services, but you can choose anything else).
+6. Use **Content Delivery Network** as an additional level of DDoS prevention: it can securely cache your media and let it be accessible from a wide list of servers on the Internet (part of Cloudflare services, but you can choose anything else).
+7. Configure a **Rate Limit** of allowed number of requests from a user during specified time range to protect against DDoS attacks.
+8. Set up an **Intrusion Detection/Prevention System** to monitor network traffic for suspicious activity and issue alerts when such activity is discovered.
+
+Setting up and configuring of all these protections depends on and requires a lot of info about your local network and self-hosted services. Based on this info, the configuration flow and resulting services architecture might differ a lot between cases. That is why in the scope of this project, we can only provide you with this high-level list of possible ways of webservice protection. You'll need to investigate them, find the best combination and configuration for your case, and take responsibility to configure everything in the correct and consistent way. We cannot provide you support for such highly secured setups, as a lot of things might work differently because of security limitations.
+
 ## Set up development environment
 
 ### Local setup
diff --git a/docker-compose example/Makefile b/docker-compose example/Makefile
new file mode 100644
index 0000000..c7c47a3
--- /dev/null
+++ b/docker-compose example/Makefile	
@@ -0,0 +1,116 @@
+MAKEFLAGS += --always-make
+
+-include .env
+export
+
+## Ensure compatibility with "docker-compose" (old) and "docker compose" (new).
+HAS_DOCKER_COMPOSE_NO_DASH := $(shell docker compose version)
+ifdef HAS_DOCKER_COMPOSE_NO_DASH
+    DOCKER_COMPOSE=docker compose
+else
+    DOCKER_COMPOSE=docker-compose
+endif
+## If you want to use only the new compose command, comment previous section
+## and uncomment next line:
+# DOCKER_COMPOSE=docker compose
+
+help:
+	@echo 'PhotoView Docker Compose management scenarios simplification'
+	@echo 'USAGE:'
+	@echo 'make <target>'
+	@echo ''
+	@echo 'Targets:'
+	@echo '   help      Prints this usage info.'
+	@echo '   all       Pulls fresh Docker images from the Registry and (re)starts the service.'
+	@echo '             Useful for the 1st start or update scenarios.'
+	@echo '   update    The same as `all`, created for convenience.'
+	@echo '   start     Creates folders for service data in the ${HOST_PHOTOVIEW_LOCATION} if not exist,'
+	@echo '             and starts the service. Optionally runs a Docker system cleanup, if uncommented.'
+	@echo '   stop      Just stops the service, keeping all containers and volumes in Docker.'
+	@echo '   restart   Simply stops and starts the service.'
+	@echo '   backup    Verifies service database and creates new service backup'
+	@echo '             in the ${HOST_PHOTOVIEW_BACKUP}/<date of execution> using .tar.xz by default.'
+	@echo '             If you want to use 7zz instead (which is faster), read the comment on top of the target script.'
+	@echo '   pull      Pulls fresh Docker images from the Registry.'
+	@echo '   readable  Makes sure that the ${HOST_PHOTOVIEW_MEDIA_ROOT} and all its files and subdirectories'
+	@echo '             are searchable and readable by other users.'
+	@echo '   terminal  Starts a Bash shell session inside the `photoview` container for troubleshooting.'
+	@echo '   logs      Shows the last 100 lines (if the command not modified) from the log,'
+	@echo '             stays listening for new lines, and shows them interactively. Ctrl + C to exit.'
+	@echo '             It can be used as a source for a log viewer, like "make logs | lnav", so that you can'
+	@echo '             interactively filter and search for needed info.'
+	@echo '   down      The same as `stop`, but also removes containers and volumes from Docker. Your data is safe.'
+	@echo '   remove    Removes the service from Docker, including all items.'
+	@echo '   uninstall Stops and removes the service from Docker, including all items.'
+	@echo ''
+all: pull restart
+uninstall: down remove
+restart: stop start
+update: pull restart
+pull:
+	$(DOCKER_COMPOSE) pull --ignore-pull-failures
+start:
+	@## The next line is for MariaDB. If you use SQLite or PostgreSQL, comment it out and uncomment the corresponding line instead
+	mkdir -p ${HOST_PHOTOVIEW_LOCATION}/database/mariadb
+	@## The next line is for SQLite
+	@# mkdir -p ${HOST_PHOTOVIEW_LOCATION}/database
+	@## The next line is for PostgreSQL
+	@# mkdir -p ${HOST_PHOTOVIEW_LOCATION}/database/postgres
+	mkdir -p ${HOST_PHOTOVIEW_LOCATION}/storage
+	chmod 777 ${HOST_PHOTOVIEW_LOCATION}/storage
+	$(DOCKER_COMPOSE) up -d --remove-orphans
+	@## Uncomment the next line if you want to run an automatic cleanup of Docker leftovers
+	@## Make sure to read the Docker documentation to understand how it works
+	@## Please note that this command is applied to the Docker host affecting all hosted services, not only the PhotoView
+	@# docker system prune -f
+stop:
+	$(DOCKER_COMPOSE) stop
+down:
+	$(DOCKER_COMPOSE) down -v
+remove:
+	$(DOCKER_COMPOSE) rm -s -v
+terminal:
+	$(DOCKER_COMPOSE) exec photoview bash
+logs:
+	$(DOCKER_COMPOSE) logs --tail=100 -f
+readable:
+	find ${HOST_PHOTOVIEW_MEDIA_ROOT} -type d -exec chmod o+rx {} \;
+	find ${HOST_PHOTOVIEW_MEDIA_ROOT} -type f -exec chmod o+r {} \;
+## By default the `backup` uses MariaDB. If you use SQLite or PostgreSQL, replace the `backup-mariadb` with corresponding target
+## keeping the order of the calls the same
+## -----------------------
+## To see the content of the *.tar.xz use the command `tar -tvJf archive_name.tar.xz`
+## To unpack the *.tar.xz into current folder use the command `tar -xJf archive_name.tar.xz`
+## -----------------------
+## The backup script creates .tar.xz archives. This type of archives provides great compression rate, but utilizes a lot of
+## resources and time. It was selected, because it is pre-installed on most distros.
+## However, you could replace it with the 7zz, which uses much less resources with comparable compression rate.
+## Make sure to install the 7zz first and then comment out the 2 lines with tar command (1st one in the corresponding DB backup
+## target, 2nd one in the `backup-post` target), uncomment corresponding lines with 7zz command
+backup: backup-pre backup-mariadb backup-post
+backup-pre:
+	mkdir -p ${HOST_PHOTOVIEW_BACKUP}
+	mkdir ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`
+backup-mariadb:
+	$(DOCKER_COMPOSE) exec mariadb mysqlcheck -u root --password=${MARIADB_ROOT_PASSWORD} --check --check-upgrade --flush --process-views=YES --auto-repair --all-databases
+	$(DOCKER_COMPOSE) exec mariadb mysqlcheck -u root --password=${MARIADB_ROOT_PASSWORD} --optimize --flush --auto-repair --all-databases
+	$(DOCKER_COMPOSE) exec mariadb mariadb-dump -u root --password=${MARIADB_ROOT_PASSWORD} -e -x --all-databases -- > ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/mariaDB_mysql_dump.sql
+	tar -cJf ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/mariaDB_mysql_dump.tar.xz ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/mariaDB_mysql_dump.sql --remove-files
+	@# 7zz a -mx=9 ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/mariaDB_mysql_dump.7z ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/mariaDB_mysql_dump.sql && rm ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/mariaDB_mysql_dump.sql
+backup-sqlite:
+	$(DOCKER_COMPOSE) exec photoview sh -c 'echo "PRAGMA integrity_check;" | sqlite3 ${PHOTOVIEW_SQLITE_PATH}'
+	$(DOCKER_COMPOSE) exec photoview sh -c 'echo "VACUUM;" | sqlite3 ${PHOTOVIEW_SQLITE_PATH}'
+	$(DOCKER_COMPOSE) exec photoview sh -c 'echo ".backup \"${PHOTOVIEW_SQLITE_PATH}_backup.db\"" | sqlite3 ${PHOTOVIEW_SQLITE_PATH}'
+	tar -cJf ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/sqlite_backup.tar.xz ${HOST_PHOTOVIEW_LOCATION}/database/photoview.db_backup.db && rm ${HOST_PHOTOVIEW_LOCATION}/database/photoview.db_backup.db
+	@# 7zz a -mx=9 ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/sqlite_backup.7z ${HOST_PHOTOVIEW_LOCATION}/database/photoview.db_backup.db && rm ${HOST_PHOTOVIEW_LOCATION}/database/photoview.db_backup.db
+backup-postgres:
+	$(DOCKER_COMPOSE) exec postgres sh -c 'PGPASSWORD=${PGSQL_PASSWORD} psql -U ${PGSQL_USER} -d ${PGSQL_DATABASE} -c "VACUUM (VERBOSE, ANALYZE)"'
+	$(DOCKER_COMPOSE) exec postgres sh -c 'PGPASSWORD=${PGSQL_PASSWORD} pg_dump -U ${PGSQL_USER} -c --if-exists -C -v ${PGSQL_DATABASE} --' > ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/postgres_pg_dump.sql
+	tar -cJf ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/postgres_pg_dump.tar.xz ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/postgres_pg_dump.sql && rm ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/postgres_pg_dump.sql
+	@# 7zz a -mx=9 ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/postgres_pg_dump.7z ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/postgres_pg_dump.sql && rm ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/postgres_pg_dump.sql
+backup-post:
+	cp ${HOST_PHOTOVIEW_LOCATION}/Makefile ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/
+	cp ${HOST_PHOTOVIEW_LOCATION}/docker-compose.yml ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/
+	cp ${HOST_PHOTOVIEW_LOCATION}/.env ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/
+	tar -cJf ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/storage.tar.xz ${HOST_PHOTOVIEW_LOCATION}/storage
+	@# 7zz a -mx=9 ${HOST_PHOTOVIEW_BACKUP}/`date +%Y-%m-%d`/storage.7z ${HOST_PHOTOVIEW_LOCATION}/storage
diff --git a/docker-compose example/docker-compose.example.yml b/docker-compose example/docker-compose.example.yml
new file mode 100644
index 0000000..edfa3ad
--- /dev/null
+++ b/docker-compose example/docker-compose.example.yml	
@@ -0,0 +1,166 @@
+services:
+  photoview:
+    image: viktorstrate/photoview:2
+    labels:
+      - "com.centurylinklabs.watchtower.enable=true"
+    hostname: photoview
+    container_name: photoview
+    restart: unless-stopped
+    stop_grace_period: 10s
+    ports:
+      - "8000:80" ## HTTP port (host:container)
+    ## This ensures that DB is initialized and ready for connections.
+    ## Comment out the entire `depends_on` section if PHOTOVIEW_DATABASE_DRIVER is set to `sqlite` in the .env
+    ## Or comment out the `mariadb:` and uncomment the `postgres:` if PHOTOVIEW_DATABASE_DRIVER is set to `postgres`
+    depends_on:
+      # postgres:
+      mariadb:
+        condition: service_healthy
+    ## Security options for some restricted systems
+    security_opt:
+      - seccomp:unconfined
+      - apparmor:unconfined
+    environment:
+      PHOTOVIEW_DATABASE_DRIVER: ${PHOTOVIEW_DATABASE_DRIVER}
+      ## Comment out the next variable in the case PHOTOVIEW_DATABASE_DRIVER is set to `sqlite` or `postgres` in the .env
+      PHOTOVIEW_MYSQL_URL: "${MARIADB_USER}:${MARIADB_PASSWORD}@tcp(photoview-mariadb)/${MARIADB_DATABASE}"
+      ## Uncomment the next line if PHOTOVIEW_DATABASE_DRIVER is set to `sqlite` in the .env
+      # PHOTOVIEW_SQLITE_PATH: ${PHOTOVIEW_SQLITE_PATH}
+      ## Uncomment the next line if PHOTOVIEW_DATABASE_DRIVER is set to `postgres` in the .env
+      # PHOTOVIEW_POSTGRES_URL: postgres://${PGSQL_USER}:${PGSQL_PASSWORD}@photoview-pgsql:5432/${PGSQL_DATABASE}?sslmode=${PGSQL_SSL_MODE}
+      PHOTOVIEW_LISTEN_IP: "photoview"
+      ## Uncomment the next variable and set a different value to change the port photoview uses inside the container.
+      ## If you change this, remember to update the port mapping (under the `ports:`) above!
+      # PHOTOVIEW_LISTEN_PORT: 80
+      ## Uncomment the next variable and set a different value to set the location of the media cache inside the container.
+      ## If you change this, remember to update the right side of the storage volume mount (under the `volumes:`) below!
+      # PHOTOVIEW_MEDIA_CACHE: "/home/photoview/media-cache"
+      ## Optional: If you are using Samba/CIFS-Share and experience problems with "directory not found"
+      ## Enable the following Godebug
+      # - GODEBUG=asyncpreemptoff=1
+      ## Optional: To enable map related features, you need to create a mapbox token.
+      ## A token can be generated for free here https://account.mapbox.com/access-tokens/
+      ## It's a good idea to limit the scope of the token to your own domain, to prevent others from using it.
+      MAPBOX_TOKEN: ${MAPBOX_TOKEN}
+    ## Share hardware devices with FFmpeg (optional):
+    # devices:
+      ## Uncomment next devices mappings if they are available in your host system
+      ## Intel QSV
+      # - "/dev/dri:/dev/dri"
+      ## Nvidia CUDA
+      # - "/dev/nvidia0:/dev/nvidia0"
+      # - "/dev/nvidiactl:/dev/nvidiactl"
+      # - "/dev/nvidia-modeset:/dev/nvidia-modeset"
+      # - "/dev/nvidia-nvswitchctl:/dev/nvidia-nvswitchctl"
+      # - "/dev/nvidia-uvm:/dev/nvidia-uvm"
+      # - "/dev/nvidia-uvm-tools:/dev/nvidia-uvm-tools"
+      ## Video4Linux Video Encode Device (h264_v4l2m2m)
+      # - "/dev/video11:/dev/video11"
+    volumes:
+      ## Example:
+      ## - "/host/folder:/container/folder"
+      - "/etc/localtime:/etc/localtime:ro" ## use local time from host
+      - "/etc/timezone:/etc/timezone:ro"   ## use timezone from host
+      ## Uncomment the next line if PHOTOVIEW_DATABASE_DRIVER is set to `sqlite` in the .env
+      # - "${HOST_PHOTOVIEW_LOCATION}/database:/home/photoview/database"
+      - "${HOST_PHOTOVIEW_LOCATION}/storage:/home/photoview/media-cache"
+      ## Change This in the .env file: to the directory where your photos are located on your server.
+      ## You can mount multiple paths if your photos are spread across multiple directories.
+      ## The same path as the container path set here, you'll need to provide on the Photoview's init page (the one between the ':' chars).
+      ## If you mount several folders, provide the path to the parent one on the init page.
+      ## If you mount several folders, make sure that there are no direct mappings to the media root folder.
+      ## This means that you need to also modify the container path of the HOST_PHOTOVIEW_MEDIA_ROOT
+      ## to something like '/photos/main'. Note that this new name ('main' in this example) will become an album in Photoview.
+      - "${HOST_PHOTOVIEW_MEDIA_ROOT}:/photos:ro"
+      ## *Additional* media folders can be mounted like this (set the variable in .env file)
+      ## Note that a mount cannot be located in a subfolder of another mount.
+      # - "${HOST_PHOTOVIEW_MEDIA_FAMILY}:/photos/Family:ro"
+
+  ## Watchtower upgrades services automatically (optional)
+  watchtower:
+    image: containrrr/watchtower:latest
+    hostname: watchtower
+    container_name: watchtower
+    restart: unless-stopped
+    environment:
+      ## Comment out the next variable if you want Watchtower to auto-update all containers, running on the host,
+      ## while now it will update only those with the label "com.centurylinklabs.watchtower.enable=true"
+      WATCHTOWER_LABEL_ENABLE: true
+      WATCHTOWER_CLEANUP: ${WATCHTOWER_CLEANUP}
+      WATCHTOWER_POLL_INTERVAL: ${WATCHTOWER_POLL_INTERVAL}
+      WATCHTOWER_TIMEOUT: ${WATCHTOWER_TIMEOUT}
+    volumes:
+      - "/var/run/docker.sock:/var/run/docker.sock"
+      - "~/.docker/config.json:/config.json:ro" ## optional, for authentication if you have a Docker Hub account
+      - "/etc/localtime:/etc/localtime:ro"      ## use local time from host
+      - "/etc/timezone:/etc/timezone:ro"        ## use timezone from host
+
+  ## Comment out the `mariadb` service if PHOTOVIEW_DATABASE_DRIVER is set to `sqlite` or `postgres` in the .env
+  mariadb:
+    image: mariadb:lts
+    labels:
+      - "com.centurylinklabs.watchtower.enable=true"
+    hostname: photoview-mariadb
+    container_name: photoview-mariadb
+    restart: unless-stopped
+    stop_grace_period: 5s
+    ## Optimized MariaDB startup command for better performance and compatibility
+    command: mariadbd --innodb-buffer-pool-size=512M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
+    security_opt: ## see https://github.com/MariaDB/mariadb-docker/issues/434#issuecomment-1136151239
+      - seccomp:unconfined
+      - apparmor:unconfined
+    ## Uncomment next 2 lines if you want to access the database directly
+    # ports:
+      # - "3306:3306"
+    environment:
+      MARIADB_AUTO_UPGRADE: "1"
+      MARIADB_DATABASE: ${MARIADB_DATABASE}
+      MARIADB_USER: ${MARIADB_USER}
+      MARIADB_PASSWORD: ${MARIADB_PASSWORD}
+      MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
+    volumes:
+      ## Example:
+      ## - "/host/folder:/container/folder"
+      - "/etc/localtime:/etc/localtime:ro" ## use local time from host
+      - "/etc/timezone:/etc/timezone:ro"   ## use timezone from host
+      - "${HOST_PHOTOVIEW_LOCATION}/database/mariadb:/var/lib/mysql" ## DO NOT REMOVE
+    healthcheck:
+      test: mysqladmin ping -s -h localhost -u $$MARIADB_USER --password=$$MARIADB_PASSWORD
+      interval: 1m
+      timeout: 5s
+      retries: 5
+      start_period: 3m
+
+  ## Uncomment the `postgres` service if PHOTOVIEW_DATABASE_DRIVER is set to `postgres` in the .env
+#  postgres:
+#    image: postgres:16-alpine
+#    labels:
+#      - "com.centurylinklabs.watchtower.enable=true"
+#    hostname: photoview-pgsql
+#    container_name: photoview-pgsql
+#    restart: unless-stopped
+#    stop_grace_period: 5s
+#    ## Security options for some restricted systems
+#    security_opt:
+#      - seccomp:unconfined
+#      - apparmor:unconfined
+#    ## Uncomment next 2 lines if you want to access the database directly
+#    # ports:
+#      # - 5432:5432
+#    environment:
+#      POSTGRES_DB: ${PGSQL_DATABASE}
+#      POSTGRES_USER: ${PGSQL_USER}
+#      POSTGRES_PASSWORD: ${PGSQL_PASSWORD}
+#      ## See other optional variables in the https://hub.docker.com/_/postgres
+#    volumes:
+#      ## Example:
+#      ## - "/host/folder:/container/folder"
+#      - "/etc/localtime:/etc/localtime:ro" ## use local time from host
+#      - "/etc/timezone:/etc/timezone:ro"   ## use timezone from host
+#      - "${HOST_PHOTOVIEW_LOCATION}/database/postgres:/var/lib/postgresql/data" ## DO NOT REMOVE
+#    healthcheck:
+#      test: pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB
+#      interval: 1m
+#      timeout: 5s
+#      retries: 5
+#      start_period: 3m
diff --git a/docker-compose example/docker-compose.minimal.example.yml b/docker-compose example/docker-compose.minimal.example.yml
new file mode 100644
index 0000000..9809e90
--- /dev/null
+++ b/docker-compose example/docker-compose.minimal.example.yml	
@@ -0,0 +1,82 @@
+services:
+  photoview:
+    image: viktorstrate/photoview:2
+    hostname: photoview
+    container_name: photoview
+    restart: unless-stopped
+    stop_grace_period: 10s
+    ports:
+      - "8000:80" ## HTTP port (host:container)
+    ## This ensures that DB is initialized and ready for connections.
+    ## Comment out the entire `depends_on` section if PHOTOVIEW_DATABASE_DRIVER is set to `sqlite` in the .env
+    depends_on:
+      mariadb:
+        condition: service_healthy
+    ## Security options for some restricted systems
+    security_opt:
+      - seccomp:unconfined
+      - apparmor:unconfined
+    environment:
+      PHOTOVIEW_DATABASE_DRIVER: ${PHOTOVIEW_DATABASE_DRIVER}
+      ## Comment out the next variable in the case PHOTOVIEW_DATABASE_DRIVER is set to `sqlite` or `postgres` in the .env
+      PHOTOVIEW_MYSQL_URL: "${MARIADB_USER}:${MARIADB_PASSWORD}@tcp(photoview-mariadb)/${MARIADB_DATABASE}"
+      ## Uncomment the next line if PHOTOVIEW_DATABASE_DRIVER is set to `sqlite` in the .env
+      # PHOTOVIEW_SQLITE_PATH: ${PHOTOVIEW_SQLITE_PATH}
+      PHOTOVIEW_LISTEN_IP: "photoview"
+      ## Optional: If you are using Samba/CIFS-Share and experience problems with "directory not found"
+      ## Enable the following Godebug
+      # - GODEBUG=asyncpreemptoff=1
+      ## Optional: To enable map related features, you need to create a mapbox token.
+      ## A token can be generated for free here https://account.mapbox.com/access-tokens/
+      ## It's a good idea to limit the scope of the token to your own domain, to prevent others from using it.
+      MAPBOX_TOKEN: ${MAPBOX_TOKEN}
+    volumes:
+      ## Example:
+      ## - "/host/folder:/container/folder"
+      - "/etc/localtime:/etc/localtime:ro" ## use local time from host
+      - "/etc/timezone:/etc/timezone:ro"   ## use timezone from host
+      ## Uncomment the next line if PHOTOVIEW_DATABASE_DRIVER is set to `sqlite` in the .env
+      # - "${HOST_PHOTOVIEW_LOCATION}/database:/home/photoview/database"
+      - "${HOST_PHOTOVIEW_LOCATION}/storage:/home/photoview/media-cache"
+      ## Change This in the .env file: to the directory where your photos are located on your server.
+      ## You can mount multiple paths if your photos are spread across multiple directories.
+      ## The same path as the container path set here, you'll need to provide on the Photoview's init page (the one between the ':' chars).
+      ## If you mount several folders, provide the path to the parent one on the init page.
+      ## If you mount several folders, make sure that there are no direct mappings to the media root folder.
+      ## This means that you need to also modify the container path of the HOST_PHOTOVIEW_MEDIA_ROOT
+      ## to something like '/photos/main'. Note that this new name ('main' in this example) will become an album in Photoview.
+      - "${HOST_PHOTOVIEW_MEDIA_ROOT}:/photos:ro"
+      ## *Additional* media folders can be mounted like this (set the variable in .env file)
+      ## Note that a mount cannot be located in a subfolder of another mount.
+      # - "${HOST_PHOTOVIEW_MEDIA_FAMILY}:/photos/Family:ro"
+
+  ## Comment out the `mariadb` service if PHOTOVIEW_DATABASE_DRIVER is set to `sqlite` or `postgres` in the .env
+  mariadb:
+    image: mariadb:lts
+    hostname: photoview-mariadb
+    container_name: photoview-mariadb
+    restart: unless-stopped
+    stop_grace_period: 5s
+    ## Optimized MariaDB startup command for better performance and compatibility
+    command: mariadbd --innodb-buffer-pool-size=512M --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=120
+    security_opt: ## see https://github.com/MariaDB/mariadb-docker/issues/434#issuecomment-1136151239
+      - seccomp:unconfined
+      - apparmor:unconfined
+    environment:
+      MARIADB_AUTO_UPGRADE: "1"
+      MARIADB_DATABASE: ${MARIADB_DATABASE}
+      MARIADB_USER: ${MARIADB_USER}
+      MARIADB_PASSWORD: ${MARIADB_PASSWORD}
+      MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
+    volumes:
+      ## Example:
+      ## - "/host/folder:/container/folder"
+      - "/etc/localtime:/etc/localtime:ro" ## use local time from host
+      - "/etc/timezone:/etc/timezone:ro"   ## use timezone from host
+      - "${HOST_PHOTOVIEW_LOCATION}/database/mariadb:/var/lib/mysql" ## DO NOT REMOVE
+    healthcheck:
+      test: mysqladmin ping -s -h localhost -u $$MARIADB_USER --password=$$MARIADB_PASSWORD
+      interval: 1m
+      timeout: 5s
+      retries: 5
+      start_period: 3m
diff --git a/docker-compose example/example.env b/docker-compose example/example.env
new file mode 100644
index 0000000..a6505a2
--- /dev/null
+++ b/docker-compose example/example.env	
@@ -0,0 +1,62 @@
+##================***================##
+## These are the environment setup variables.
+## Start setting up your instance from here.
+## Syntax of the .env file is next:
+## VARIABLE_NAME=variable value with everything after the '=' and till the end of the line.
+## The variables with values, set in the docker-compose.yml directly, are for advanced configuration.
+##================***================##
+
+##----------Host variables-----------##
+## This is the current folder, where all Photoview files and folders (except of your media library) are located
+HOST_PHOTOVIEW_LOCATION=/opt/photoview
+
+## This is where your original photos and videos located.
+## Provide here the path to single root folder for your media collection.
+HOST_PHOTOVIEW_MEDIA_ROOT=/your/root/media/folder
+## If you'd like to map multiple folders from different locations, create additional variables
+## here like the next one and modify the docker-compose.yml to match them and use in volume mappings.
+# HOST_PHOTOVIEW_MEDIA_FAMILY=/full/path/to/folder
+
+## This is where the Photoview data will be backed up
+HOST_PHOTOVIEW_BACKUP=/media/Backup/PhotoView
+##-----------------------------------##
+
+##-------Photoview variables---------##
+## PHOTOVIEW_DATABASE_DRIVER could have one of values: `mysql` (default), `sqlite`, `postgres`
+PHOTOVIEW_DATABASE_DRIVER=mysql
+
+## Optional: To enable map related features, you need to create a mapbox token.
+## A token can be generated for free here https://account.mapbox.com/access-tokens/
+## It's a good idea to limit the scope of the token to your own domain, to prevent others from using it.
+# MAPBOX_TOKEN=yourToken
+##-----------------------------------##
+
+##--------MariaDB variables----------##
+## Comment out these variables if PHOTOVIEW_DATABASE_DRIVER is `sqlite` or `postgres`
+## Use password generator to generate secret values and replace these defaults
+MARIADB_DATABASE=photoview
+MARIADB_USER=photoview
+MARIADB_PASSWORD=photosecret
+MARIADB_ROOT_PASSWORD=superphotosecret
+##-----------------------------------##
+
+##---------SQLite variables----------##
+## Uncomment the next line if PHOTOVIEW_DATABASE_DRIVER is `sqlite`
+# PHOTOVIEW_SQLITE_PATH=/home/photoview/database/photoview.db
+##-----------------------------------##
+
+##-------PostgreSQL variables--------##
+## Uncomment the next lines if PHOTOVIEW_DATABASE_DRIVER is `postgres`
+# PGSQL_DATABASE=photoview
+# PGSQL_USER=photoview
+# PGSQL_PASSWORD=superphotosecret
+## See https://www.postgresql.org/docs/current/libpq-ssl.html for possible ssl modes
+# PGSQL_SSL_MODE=prefer
+##-----------------------------------##
+
+##-------Watchtower variables--------##
+## The POLL_INTERVAL in sec
+WATCHTOWER_POLL_INTERVAL=86400
+WATCHTOWER_TIMEOUT=30s
+WATCHTOWER_CLEANUP=true
+##\\\\\\\\\\\\\\\\\//////////////////##
diff --git a/docker-compose.example.yml b/docker-compose.example.yml
deleted file mode 100644
index 773bcab..0000000
--- a/docker-compose.example.yml
+++ /dev/null
@@ -1,58 +0,0 @@
-version: "3"
-
-services:
-  db:
-    image: mariadb:10.5
-    restart: always
-    environment:
-      - MYSQL_DATABASE=photoview
-      - MYSQL_USER=photoview
-      - MYSQL_PASSWORD=photosecret
-      - MYSQL_RANDOM_ROOT_PASSWORD=1
-    volumes:
-      - db_data:/var/lib/mysql
-
-  photoview:
-    image: viktorstrate/photoview:2
-    restart: always
-    ports:
-      - "8000:80"
-    depends_on:
-      - db
-
-    environment:
-      - PHOTOVIEW_DATABASE_DRIVER=mysql
-      - PHOTOVIEW_MYSQL_URL=photoview:photosecret@tcp(db)/photoview
-      - PHOTOVIEW_LISTEN_IP=photoview
-      - PHOTOVIEW_LISTEN_PORT=80
-      - PHOTOVIEW_MEDIA_CACHE=/app/cache
-
-      # If you want to use sqlite driver, you can get rid of the mariadb service defined above and the PHOTOVIEW_MYSQL_URL variable
-      # - PHOTOVIEW_DATABASE_DRIVER=sqlite
-      # - PHOTOVIEW_SQLITE_PATH=/app/database/photoview.db
-      
-      # Optional: If you are using Samba/CIFS-Share and experience problems with "directory not found"
-      # Enable the following Godebug
-      # - GODEBUG=asyncpreemptoff=1
-      
-      
-      # Optional: To enable map related features, you need to create a mapbox token.
-      # A token can be generated for free here https://account.mapbox.com/access-tokens/
-      # It's a good idea to limit the scope of the token to your own domain, to prevent others from using it.
-      # - MAPBOX_TOKEN=<YOUR TOKEN HERE>
-
-    volumes:
-      - api_cache:/app/cache
-
-      # Change This: to the directory where your photos are located on your server.
-      # If the photos are located at `/home/user/photos`, then change this value
-      # to the following: `/home/user/photos:/photos:ro`.
-      # You can mount multiple paths, if your photos are spread across multiple directories.
-      - ./photos_path:/photos:ro
-
-      # If using sqlite, you can bind the datbase folder to the host
-      # -  /opt/photoview/database:/app/database
-
-volumes:
-  db_data:
-  api_cache:
