diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml
index ba934c0c..ee93f611 100644
--- a/.github/workflows/pytest.yml
+++ b/.github/workflows/pytest.yml
@@ -20,9 +20,7 @@ jobs:
 
       - name: Install dependencies
         run: |
-          sudo apt install -y libmariadb3 libmariadb-dev
           python -m pip install --upgrade pip
-          pip install -r requirements.txt
           pip install -r requirements-dev.txt
 
       - name: Run Pytest
diff --git a/.gitignore b/.gitignore
index 5863b08e..a911327f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
 docs/
 data/
+cache/
+output/
 
 # Default ignore folders and files for VS Code, Python
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e4fbc3d4..229d33be 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -24,7 +24,7 @@ To make collaboration easier, we require pull requests to pass code style and un
 Our code style checks use [`pre-commit`](https://pre-commit.com).
 
 ```bash
-pip install -r requirements.txt
+pip install -r requirements-dev.txt
 ```
 
 To run formatting automatically before every commit:
@@ -36,7 +36,7 @@ pre-commit install
 Or run them manually:
 
 ```bash
-pre-commit --all
+pre-commit run --all-files
 ```
 
 ### Tests
diff --git a/Dockerfile b/Dockerfile
index dbbb1d25..87ebca6f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,23 +3,33 @@ FROM python:${PYTHON_VERSION}-slim
 
 LABEL source="https://github.com/Akkudoktor-EOS/EOS"
 
-EXPOSE 5000
+ENV VIRTUAL_ENV="/opt/venv"
+ENV PATH="${VIRTUAL_ENV}/bin:${PATH}"
+ENV MPLCONFIGDIR="/tmp/mplconfigdir"
+ENV EOS_DIR="/opt/eos"
+ENV EOS_CACHE_DIR="${EOS_DIR}/cache"
+ENV EOS_OUTPUT_DIR="${EOS_DIR}/output"
 
-WORKDIR	/opt/eos
+WORKDIR ${EOS_DIR}
 
-COPY . .
+RUN adduser --system --group --no-create-home eos \
+    && mkdir -p "${MPLCONFIGDIR}" \
+    && chown eos "${MPLCONFIGDIR}" \
+    && mkdir -p "${EOS_CACHE_DIR}" \
+    && chown eos "${EOS_CACHE_DIR}" \
+    && mkdir -p "${EOS_OUTPUT_DIR}" \
+    && chown eos "${EOS_OUTPUT_DIR}"
 
-ARG APT_OPTS="--yes --auto-remove --no-install-recommends --no-install-suggests"
+COPY requirements.txt .
 
-RUN DEBIAN_FRONTEND=noninteractive \
-	apt-get update \
-	&& apt-get install ${APT_OPTS} gcc libhdf5-dev libmariadb-dev pkg-config mariadb-common libmariadb3 \
-	&& rm -rf /var/lib/apt/lists/* \
-    && pip install --no-cache-dir -r requirements.txt \
-    && pip install --no-cache-dir build \
-    && pip install --no-cache-dir -e . \
-    && apt remove ${APT_OPTS} gcc libhdf5-dev libmariadb-dev pkg-config
+RUN --mount=type=cache,target=/root/.cache/pip \
+    pip install -r requirements.txt
 
+COPY src .
+
+USER eos
 ENTRYPOINT []
 
-CMD ["python", "-m", "akkudoktoreos.flask_server"]
+CMD ["python", "-m", "akkudoktoreosserver.flask_server"]
+
+VOLUME ["${MPLCONFIGDIR}", "${EOS_CACHE_DIR}", "${EOS_OUTPUT_DIR}"]
diff --git a/Makefile b/Makefile
index 82df14b5..17865c74 100644
--- a/Makefile
+++ b/Makefile
@@ -71,4 +71,7 @@ test:
 
 # Run entire setup on docker
 docker-run:
-	@docker compose up
+	@docker compose up --remove-orphans
+
+docker-build:
+	@docker compose build
diff --git a/README-DE.md b/README-DE.md
index a3bde4cc..8a757888 100644
--- a/README-DE.md
+++ b/README-DE.md
@@ -67,14 +67,6 @@ source .venv/bin/activate
 
 (wenn zsh verwendet wird, vor allem MacOS-Nutzende).
 
-Sollte `pip install` die mariadb-Abhängigkeit nicht installieren können,
-dann helfen folgende Kommandos:
-
-* Debian/Ubuntu: `sudo apt-get install -y libmariadb-dev`
-* Macos/Homebrew: `brew install mariadb-connector-c`
-
-gefolgt von einem erneuten `pip install -r requirements.txt`.
-
 ## Nutzung
 
 Einstellungen in `config.py` anpassen.
diff --git a/README.md b/README.md
index 45422a31..282fde3d 100644
--- a/README.md
+++ b/README.md
@@ -63,13 +63,6 @@ source .venv/bin/activate
 ```
 (if using zsh, primarily for MacOS users).
 
-If `pip install` fails to install the mariadb dependency, the following commands may help:
-
-* Debian/Ubuntu: `sudo apt-get install -y libmariadb-dev`
-* MacOS/Homebrew: `brew install mariadb-connector-c`
-
-Followed by a renewed `pip install -r requirements.txt`.
-
 ## Usage
 
 Adjust `config.py`.
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 34650d40..cdc5ad61 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -5,28 +5,18 @@ networks:
 services:
   eos:
     image: 'akkudoktor/eos:${EOS_VERSION}'
+    read_only: true
     build:
       context: .
       dockerfile: 'Dockerfile'
       args:
         PYTHON_VERSION: '${PYTHON_VERSION}'
-    depends_on:
-      - 'mariadb'
     init: true
     environment:
       FLASK_RUN_PORT: '${EOS_PORT}'
     networks:
       - 'eos'
+    volumes:
+      - ./src/akkudoktoreos/config.py:/opt/eos/akkudoktoreos/config.py:ro
     ports:
       - '${EOS_PORT}:${EOS_PORT}'
-  mariadb:
-    image: 'mariadb:${MARIADB_VERSION}-jammy'
-    environment:
-      MARIADB_ROOT_PASSWORD: '${MARIADB_ROOT_PASSWORD}'
-      MARIADB_DATABASE: '${MARIADB_DATABASE}'
-      MARIADB_USER: '${MARIADB_USER}'
-      MARIADB_PASSWORD: '${MARIADB_PASSWORD}'
-    networks:
-      - 'eos'
-    volumes:
-      - ./data/mariadb:/var/lib/mysql
diff --git a/requirements-dev.txt b/requirements-dev.txt
index b47030fb..af8093e5 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,5 +1,4 @@
-build==1.2.2.post1
+-r requirements.txt
 pytest==8.3.3
-pytest-xprocess==1.0.2
-requests==2.32.3
+pytest-cov==5.0.0
 pre-commit
diff --git a/requirements.txt b/requirements.txt
index dfa8727b..fd82718f 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,11 +1,7 @@
 numpy==2.1.2
-mariadb==1.1.10
 matplotlib==3.9.2
 flask==3.0.3
 scikit-learn==1.5.2
 deap==1.4.1
-joblib==1.4.2
 requests==2.32.3
-pytest==8.3.3
-pytest-cov==5.0.0
 pandas==2.2.3
diff --git a/src/akkudoktoreos/config.py b/src/akkudoktoreos/config.py
index 109c3b0e..9a1ee37f 100644
--- a/src/akkudoktoreos/config.py
+++ b/src/akkudoktoreos/config.py
@@ -1,5 +1,7 @@
 from datetime import datetime, timedelta
 
+output_dir = "output"
+
 prediction_hours = 48
 optimization_hours = 24
 strafe = 10
diff --git a/src/akkudoktoreos/visualize.py b/src/akkudoktoreos/visualize.py
index 727cce4a..81e59f61 100644
--- a/src/akkudoktoreos/visualize.py
+++ b/src/akkudoktoreos/visualize.py
@@ -1,4 +1,5 @@
 import datetime
+import os
 
 # Set the backend for matplotlib to Agg
 import matplotlib
@@ -7,6 +8,7 @@
 from matplotlib.backends.backend_pdf import PdfPages
 
 from akkudoktoreos.class_sommerzeit import ist_dst_wechsel
+from akkudoktoreos.config import output_dir
 
 matplotlib.use("Agg")
 
@@ -28,7 +30,10 @@ def visualisiere_ergebnisse(
     #####################
     # 24-hour visualization
     #####################
-    with PdfPages(filename) as pdf:
+    if not os.path.exists(output_dir):
+        os.makedirs(output_dir)
+    output_file = os.path.join(output_dir, filename)
+    with PdfPages(output_file) as pdf:
         # Load and PV generation
         plt.figure(figsize=(14, 14))
         plt.subplot(3, 3, 1)
diff --git a/src/akkudoktoreosserver/flask_server.py b/src/akkudoktoreosserver/flask_server.py
index c97373fd..58347030 100755
--- a/src/akkudoktoreosserver/flask_server.py
+++ b/src/akkudoktoreosserver/flask_server.py
@@ -18,7 +18,12 @@
 from akkudoktoreos.class_optimize import optimization_problem
 from akkudoktoreos.class_pv_forecast import PVForecast
 from akkudoktoreos.class_strompreis import HourlyElectricityPriceForecast
-from akkudoktoreos.config import get_start_enddate, optimization_hours, prediction_hours
+from akkudoktoreos.config import (
+    get_start_enddate,
+    optimization_hours,
+    output_dir,
+    prediction_hours,
+)
 
 app = Flask(__name__)
 
@@ -255,11 +260,11 @@ def flask_optimize():
         return jsonify(result)  # Return optimization results as JSON
 
 
-@app.route("/visualisierungsergebnisse.pdf")
+@app.route("/visualization_results.pdf")
 def get_pdf():
     # Endpoint to serve the generated PDF with visualization results
     return send_from_directory(
-        "", "visualisierungsergebnisse.pdf"
+        os.path.abspath(output_dir), "visualization_results.pdf"
     )  # Adjust the directory if needed