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):

services:
  postgres:
    image: pgvector/pgvector:pg18
    environment:
      POSTGRES_DB: memory_service
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - "55432:5432"
    volumes:
      - ../../../../deploy/keycloak/postgres-init-keycloak.sql:/docker-entrypoint-initdb.d/postgres-init-keycloak.sql:ro
    tmpfs:
      - /var/lib/postgresql
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 10

  keycloak:
    image: quay.io/keycloak/keycloak:26.6.3
    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"
      KC_HOSTNAME: http://${KEYCLOAK_HOSTNAME:-localhost}:${KEYCLOAK_HOSTNAME_PORT:-8081}
      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
    environment:
      MEMORY_SERVICE_API_KEYS_AGENT: agent-api-key-1,agent-api-key-2
      # Browser-visible local developer-console credential. Never use in production.
      MEMORY_SERVICE_API_KEYS_DEVELOPER_FRONTEND: developer-frontend-api-key
      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
      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
      MEMORY_SERVICE_OIDC_ALLOWED_CLIENTS: memory-service-client,frontend,developer-frontend
      MEMORY_SERVICE_OIDC_ALLOWED_AUDIENCES: memory-service
      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 ─────────────────────────────────────
      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

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:

  1. Detect the compose.yaml file
  2. Start all services defined in the file
  3. Wait for health checks to pass
  4. Provide connection details to your application
  5. 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