Ready-to-copy example: stacks/monitoring/agent/examples/spring-boot/.
make setup # LOKI_PUSH_USERNAME / LOKI_PUSH_PASSWORD
make monitoring
make middleware # https://devops.local/loki/# 1) copy example (or your app repo + agent config)
scp -r stacks/monitoring/agent/examples/spring-boot user@appserver:/opt/note-keeper
scp stacks/monitoring/agent/config.docker.alloy user@appserver:/opt/note-keeper/../../config.docker.alloy
# simpler: copy whole agent/ tree
scp -r stacks/monitoring/agent user@appserver:/opt/alloy-agent
# 2) trust portal CA
curl -fk https://devops.local/ca.crt -o /opt/alloy-agent/examples/spring-boot/ca.crt
# 3) env
cd /opt/alloy-agent/examples/spring-boot
cp .env.example .env
# edit LOKI_PUSH_PASSWORD, AGENT_HOST
# 4) run app + agent
docker compose up -d --buildLayout on the app host:
| Service | Role |
|---|---|
note-keeper |
your JAR; logs → stdout |
alloy-agent |
reads Docker logs via /var/run/docker.sock → https://devops.local/loki/api/v1/push |
Do not add a Loki agent inside the app Dockerfile. Alloy runs as a sidecar/host agent.
Keep your multi-stage build. Ensure the JVM writes to the console (Spring default). Optional:
ENV JAVA_TOOL_OPTIONS="-Dlogging.pattern.console=%d{ISO8601} %-5level [%thread] %logger{36} - %msg%n"Avoid logging only to files under /var/log unless you also mount them into Alloy.
Grafana → Explore → Loki:
{container="note-keeper"}
# or
{service="note-keeper"}
{host="note-keeper-prod"}
If you expose Micrometer Prometheus:
# application.yaml
management:
endpoints:
web:
exposure:
include: health,prometheus
metrics:
tags:
application: note-keeperCompose labels (already in the example):
labels:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/actuator/prometheus"(Scraping from central Prometheus still needs network reachability or Alloy remote_write — logs work without that.)