diff --git a/backend/pom.xml b/backend/pom.xml
index 9b558351..03a1e4ac 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -197,6 +197,25 @@
jsoup
1.18.1
+
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
+
+
+
+ io.micrometer
+ micrometer-tracing-bridge-otel
+
+
+
+
+ io.opentelemetry.instrumentation
+ opentelemetry-spring-boot-starter
+ 2.27.0
+
diff --git a/backend/src/main/resources/application.yaml b/backend/src/main/resources/application.yaml
index 179ed09e..6198f3d9 100644
--- a/backend/src/main/resources/application.yaml
+++ b/backend/src/main/resources/application.yaml
@@ -45,9 +45,34 @@ server:
forward-headers-strategy: native
management:
+ server:
+ # Management port is separate from the app port so that:
+ # (a) Caddy never proxies /actuator/* (it only routes :8080 → the app port)
+ # (b) Prometheus scrapes backend:8081 directly inside archiv-net, not via Caddy
+ # (c) Spring Security's session-authenticated filter chain on :8080 never sees actuator requests
+ port: 8081
+ endpoints:
+ web:
+ exposure:
+ include: health,info,prometheus,metrics
+ endpoint:
+ prometheus:
+ enabled: true
health:
mail:
enabled: false
+ tracing:
+ sampling:
+ probability: 1.0 # 100% in dev; override via MANAGEMENT_TRACING_SAMPLING_PROBABILITY in prod compose
+
+# OpenTelemetry trace export — failures are non-fatal (app starts cleanly without Tempo running)
+# The default http://localhost:4317 ensures CI compatibility when no observability stack is present.
+otel:
+ service:
+ name: familienarchiv-backend
+ exporter:
+ otlp:
+ endpoint: ${OTEL_EXPORTER_OTLP_ENDPOINT:http://localhost:4317}
springdoc:
api-docs:
diff --git a/backend/src/test/resources/application-test.yaml b/backend/src/test/resources/application-test.yaml
index 6a8cbad3..b561b54e 100644
--- a/backend/src/test/resources/application-test.yaml
+++ b/backend/src/test/resources/application-test.yaml
@@ -13,3 +13,10 @@ spring:
password: test
mail:
host: localhost
+
+# Disable trace export in tests — prevents OTLP connection attempts when no Tempo is running.
+# Sampling probability 0.0 means no spans are created, so no export is attempted.
+management:
+ tracing:
+ sampling:
+ probability: 0.0
diff --git a/docker-compose.yml b/docker-compose.yml
index 952e3074..3e42c981 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -147,8 +147,18 @@ services:
SPRING_MAIL_PROPERTIES_MAIL_SMTP_STARTTLS_ENABLE: ${MAIL_STARTTLS_ENABLE:-false}
APP_OCR_BASE_URL: http://ocr-service:8000
APP_OCR_TRAINING_TOKEN: "${OCR_TRAINING_TOKEN:-}"
+ # Observability: send traces to Tempo inside archiv-net (OTLP gRPC port 4317)
+ # Tempo is defined in docker-compose.observability.yml (future issue).
+ # OTLP failures are non-fatal — backend starts cleanly without the observability stack.
+ OTEL_EXPORTER_OTLP_ENDPOINT: http://tempo:4317
+ # 10% sampling in this compose (dev + staging) — override locally to 1.0 if needed
+ MANAGEMENT_TRACING_SAMPLING_PROBABILITY: "0.1"
ports:
- "${PORT_BACKEND}:8080"
+ # Management port — Prometheus scrapes /actuator/prometheus from inside archiv-net.
+ # Not exposed to the host; Docker service-name DNS (backend:8081) is sufficient.
+ expose:
+ - "8081"
networks:
- archiv-net
healthcheck: