Docker Compose Integration
Spring Boot 3.1+ includes built-in Docker Compose support that can automatically start and configure services defined in a compose.yaml file. This makes it easy to run Memory Service and its dependencies during development.
Overview
Spring Boot’s Docker Compose integration provides:
- Automatic service discovery - Spring Boot detects running containers
- Service connection - Automatic configuration via
ConnectionDetails - Health checks - Waits for services to be ready before starting the app
- Clean shutdown - Stops containers when the app shuts down (optional)
Setup
Add the Docker Compose dependency to your pom.xml:
<dependency>
<groupId>io.github.chirino.memory-service</groupId>
<artifactId>memory-service-spring-boot-docker-compose-starter</artifactId>
<version>999-SNAPSHOT</version>
</dependency>
Place a compose.yaml file in your project root (Spring Boot auto-detects it). The chat-spring example ships this one:
services:
postgres:
image: pgvector/pgvector:pg18
environment:
POSTGRES_DB: memory_service
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- "55432:5432"
volumes:
# Init script mount (read-only); data lives on tmpfs below so it disappears with the container
- ../../../../deploy/keycloak/postgres-init-keycloak.sql:/docker-entrypoint-initdb.d/postgres-init-keycloak.sql:ro
tmpfs:
# Ephemeral Postgres data dir; removed automatically when the container is deleted
# PG 18+ uses version-specific subdirs under /var/lib/postgresql
- /var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 10
keycloak:
image: quay.io/keycloak/keycloak:26.7.4
command: ["start-dev", "--import-realm"]
environment:
KC_BOOTSTRAP_ADMIN_USERNAME: admin
KC_BOOTSTRAP_ADMIN_PASSWORD: admin
KC_DB: postgres
KC_DB_URL_HOST: postgres
KC_DB_URL_DATABASE: keycloak
KC_DB_USERNAME: keycloak
KC_DB_PASSWORD: keycloak
KC_HTTP_RELATIVE_PATH: /
KC_HEALTH_ENABLED: "true"
# External hostname for browser redirects (configurable via env var)
KC_HOSTNAME: http://${KEYCLOAK_HOSTNAME:-localhost}:${KEYCLOAK_HOSTNAME_PORT:-8081}
# Allow internal (Docker network) backchannel communication
KC_HOSTNAME_BACKCHANNEL_DYNAMIC: "true"
volumes:
- ../../../../deploy/keycloak/memory-service-realm.json:/opt/keycloak/data/import/memory-service-realm.json:ro
depends_on:
postgres:
condition: service_healthy
ports:
- "8081:8080"
healthcheck:
test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/9000 && printf 'GET /health/ready HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n' >&3 && cat <&3 | grep -q 'UP'"]
interval: 10s
timeout: 5s
retries: 15
start_period: 30s
memory-service:
image: ghcr.io/chirino/memory-service:latest
build:
context: ../../
dockerfile: Dockerfile
environment:
# Trusted agent API keys (first value is surfaced to the Spring app via the service connection)
MEMORY_SERVICE_API_KEYS_AGENT: agent-api-key-1,agent-api-key-2
# Browser-visible local developer-console credential. Never use this key in production.
# Local-only admin client for /developer: do not add it to trusted user-ID clients or send
# X-User-ID from the console.
MEMORY_SERVICE_API_KEYS_DEVELOPER_FRONTEND: developer-frontend-api-key
# Docker port publishing cannot reach a loopback-only listener, so bind 0.0.0.0 over
# plaintext HTTP and acknowledge it (local development only).
MEMORY_SERVICE_HOST: 0.0.0.0
MEMORY_SERVICE_PLAIN_TEXT: "true"
MEMORY_SERVICE_TLS: "false"
MEMORY_SERVICE_ALLOW_NON_LOOPBACK_PLAINTEXT: "true"
# ── Self-contained storage ─────
MEMORY_SERVICE_DB_KIND: sqlite
MEMORY_SERVICE_DB_URL: file:/tmp/memory-service-dev/memory-service.db
MEMORY_SERVICE_DB_MIGRATE_AT_START: "true"
MEMORY_SERVICE_CACHE_KIND: local
# Hardened startup otherwise requires a dedicated management listener.
MEMORY_SERVICE_MANAGEMENT_ON_MAIN_LISTENER: "true"
MEMORY_SERVICE_VECTOR_KIND: sqlite
MEMORY_SERVICE_EMBEDDING_KIND: local
MEMORY_SERVICE_ATTACHMENTS_KIND: fs
MEMORY_SERVICE_ATTACHMENTS_FS_DIR: /tmp/memory-service-dev/attachments
MEMORY_SERVICE_TEMP_DIR: /tmp/memory-service-dev/tmp
# ── Authentication & authorization ─────────────────────────
MEMORY_SERVICE_OIDC_ISSUER: http://${KEYCLOAK_HOSTNAME:-localhost}:${KEYCLOAK_HOSTNAME_PORT:-8081}/realms/memory-service
MEMORY_SERVICE_OIDC_DISCOVERY_URL: http://keycloak:8080/realms/memory-service
# Development-only issuer validation; configure allowed audiences in production.
MEMORY_SERVICE_OIDC_ALLOWED_AUDIENCES: memory-service
MEMORY_SERVICE_OIDC_USER_ID_CLAIM: /preferred_username
MEMORY_SERVICE_ROLES_ADMIN_OIDC_ROLE: admin
MEMORY_SERVICE_ROLES_AUDITOR_OIDC_ROLE: auditor
MEMORY_SERVICE_ROLES_ADMIN_CLIENTS: developer_frontend
MEMORY_SERVICE_ROLES_INDEXER_CLIENTS: agent
# ── Developer Frontend ─────────────────────────────────────
# Enable the integrated developer frontend at http://localhost:8082/developer
MEMORY_SERVICE_DEVELOPER_FRONTEND_ENABLED: "true"
MEMORY_SERVICE_DEVELOPER_FRONTEND_CLIENT_ID: developer_frontend
MEMORY_SERVICE_DEVELOPER_FRONTEND_AUTH_MODE: api-key
MEMORY_SERVICE_DEVELOPER_FRONTEND_API_KEY: developer-frontend-api-key
MEMORY_SERVICE_BASE_URL: "http://localhost:8082"
MEMORY_SERVICE_CORS_ENABLED: "true"
MEMORY_SERVICE_CORS_ORIGINS: "http://localhost:3000"
# ── Encryption ─────────────────────────────────────────────
# Well-known development key; replace with a secure key in production.
MEMORY_SERVICE_ENCRYPTION_KIND: dek
MEMORY_SERVICE_ENCRYPTION_DEK_KEY: ${MEMORY_SERVICE_ENCRYPTION_DEK_KEY:-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef}
depends_on:
keycloak:
condition: service_healthy
ports:
- "8082:8080"
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/ready || exit 1"]
interval: 10s
timeout: 5s
retries: 12
# Spring Boot 3.1+ will auto-detect this compose file when running from this directory. The relative volume paths and the build section refer to this repository’s layout. When you copy the file into your own project, point the volumes at your own Keycloak realm and database init script, and remove the build section so Compose uses the published image.
This configuration uses self-contained storage for the memory service (SQLite, local cache, filesystem attachments). The integrated developer console at http://localhost:8082/developer skips OAuth and uses its dedicated API-key admin client. Keycloak remains available for the Spring chat application’s end-user login flow.
The developer console key is deliberately exposed through /developer/config.json, so anyone who can reach port 8082 receives local admin access. This is appropriate only for isolated local development and must never be enabled in production. The console does not send X-User-ID; that header selects user context for normal user APIs and is not a credential for Admin APIs.
Key features:
- SQLite for memory service database storage (ephemeral)
- Local cache for in-memory caching
- Filesystem for attachment storage
- Local embeddings for vector operations
- Keycloak for the chat application’s OIDC authentication (uses Postgres)
- Integrated developer frontend at
http://localhost:8082/developer
Access points:
- Memory service API:
http://localhost:8082 - Developer frontend:
http://localhost:8082/developer - Keycloak admin console:
http://localhost:8081(admin/admin) - API key:
agent-api-key-1
Service Connection
Spring Boot automatically detects the memory-service container and provides connection details via MemoryServiceConnectionDetails. The starter uses these connection
details to configure the REST and gRPC clients.
Make sure you comment out the memory-service.client properties in your application.properties file to pickup the connection details from the Docker Compose file.
# These will override Docker Compose connection details if set
#memory-service.client.url=http://localhost:8082
#memory-service.client.api-key=agent-api-key-1
Running the Application
When you start your Spring Boot application:
mvn spring-boot:run
Spring Boot will:
- Detect the
compose.yamlfile - Start all services defined in the file
- Wait for health checks to pass
- Provide connection details to your application
- Start your application
Disabling Docker Compose
To disable Docker Compose integration (e.g., when running against an external service):
spring.docker.compose.enabled=false
Or set the environment variable:
export SPRING_DOCKER_COMPOSE_ENABLED=false
Custom Compose File Location
If your compose.yaml is in a different location:
spring.docker.compose.file=./docker/compose.yaml