Using Docker Compose as Backend for Local Development

Part 1 β€” SoVisu+ development

This section explains how to use the Docker Compose stack strictly as a backend while running SoVisu+ locally (Next.js dev server) for frontend development.


🎯 Goal

Run all shared services (Neo4j, message bus, Keycloak, APIs, databases) in Docker, but run SoVisu+ on your host machine. To make this work, you must:

  1. Map the necessary service ports from containers to the host.
  2. Use a dedicated profile (e.g. sovisuplus-db) to start only SoVisu+ backend services.
  3. Point your local SoVisu+ to these services via env vars and /etc/hosts.

🧱 Required Hostnames

Add these entries to your /etc/hosts (if not already done in the main guide):

127.0.0.1 sovisuplus.local
127.0.0.1 keycloak.local

SoVisu+ uses ORCID OAuth, which requires valid hostnames even for sandbox keys.


πŸ”“ Open the Right Ports in Compose

You’ll run SoVisu+ locally, so your host needs to reach the containers. Make sure the following port mappings are enabled.

1) SoVisu+ database (docker/sovisuplus/sovisuplus.yaml)

Uncomment the ports section so Postgres is reachable from your host:

sovisuplus.yaml
services:
  svp-db:
    # Overriden by environment specific YAML configurations (e.g. docker-compose.dev.yaml)
    # image: postgres:16
    container_name: svp-db
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${SVP_DB_USER}
      POSTGRES_PASSWORD: ${SVP_DB_PASSWORD}
      POSTGRES_DB: ${SVP_DB_NAME}
    expose:
      - 5432
    #    ports:
    #      - 5432:5432
    volumes:
      - ./postgres-data:/var/lib/postgresql/data
    networks:
      - svp-network
    healthcheck:
      test: [ 'CMD-SHELL', 'pg_isready -d ${SVP_DB_NAME} -U ${SVP_DB_USER}' ]
      interval: 1s
      timeout: 5s
      retries: 10
    command: [ "postgres","-c","max_connections=400","-c","superuser_reserved_connections=3" ]
    profiles:
      - sovisuplus-db

  sovisuplus:
    # Overriden by environment specific YAML configurations (e.g. docker-compose.dev.yaml)
    # image: crisalidesr/sovisuplus:latest
    container_name: sovisuplus
    restart: unless-stopped
    ports:
      - ${SVP_PORT:-3000}:3000
      - ${SVP_WS_INTERNAL_PORT:-3001}:3001
    environment:
      - INIT_ROLES_ON_START=true
      - RBAC_ROLES_FILE=/config/rbac.roles.yaml
      - DB_HOST=${SVP_DB_HOST:-svp-db}
      - DB_NAME=${SVP_DB_NAME}
      - DB_USER=${SVP_DB_USER}
      - DB_PASSWORD=${SVP_DB_PASSWORD}
      - DB_PORT=5432
      - APP_URL=${SOVISUPLUS_URL}
      - NEXT_PUBLIC_BASE_URL=${SOVISUPLUS_URL}
      - NEXT_PUBLIC_WS_SCHEME=${SVP_WS_SCHEME}
      - NEXT_PUBLIC_WS_HOST=${SVP_WS_HOST}
      - NEXT_PUBLIC_WS_PORT=${SVP_WS_PORT}
      - NEXT_PUBLIC_WS_PATH=${SVP_WS_PATH}
      - NEXTAUTH_URL=${SOVISUPLUS_URL}/api/auth
      - NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
      - JWT_TOKEN_EXPIRATION_HOURS=${JWT_TOKEN_EXPIRATION_HOURS:-12}
      - AMQP_HOST=${CRISALID_BUS_HOST:-crisalid-bus}
      - AMQP_PORT=${CRISALID_BUS_AMQP_PORT}
      - AMQP_USER=${CRISALID_BUS_USER}
      - AMQP_PASSWORD=${CRISALID_BUS_PASSWORD}
      - AMQP_QUEUE_NAME=${SVP_AMQP_QUEUE_NAME}
      - AMQP_EXCHANGE_NAME=${SVP_AMQP_EXCHANGE_NAME}
      - GRAPHQL_ENDPOINT_ENABLED=${GRAPHQL_ENDPOINT_ENABLED}
      - GRAPHQL_ENDPOINT_URL=http://${APOLLO_HOST:-apollo}:${APOLLO_API_PORT}/graphql
      - GRAPHQL_API_KEY_ENABLED=${APOLLO_ENABLE_API_KEYS}
      - GRAPHQL_API_KEY=${SOVISUPLUS_GRAPHQL_API_KEY}
      - KEYCLOAK_CLIENT_ID=${SOVISUPLUS_KEYCLOAK_CLIENT_ID}
      - KEYCLOAK_CLIENT_SECRET=${SOVISUPLUS_KEYCLOAK_CLIENT_SECRET}
      - KEYCLOAK_PUBLIC_ADDR=${KEYCLOAK_SCHEME}://${KEYCLOAK_HOST}:${KEYCLOAK_PORT}
      - KEYCLOAK_INTERNAL_ADDR=http://keycloak:8080
      - KEYCLOAK_REALM=${KEYCLOAK_REALM}
      - ORCID_URL=${ORCID_URL}
      - ORCID_SCOPES=${ORCID_SCOPES}
      - ORCID_CLIENT_ID=${ORCID_CLIENT_ID}
      - ORCID_CLIENT_SECRET=${ORCID_CLIENT_SECRET}
      - VOCABS_URL=${CRISALID_VOCAB_SEARCH_URL}
      - NEXT_PUBLIC_AVAILABLE_VOCABS=${CRISALID_VOCAB_SEARCH_AVAILABLE_VOCABS:-jel,aat,acm,elsst,pactols,euroscivoc}
      - PERSPECTIVE_ROLES_FILTER=${PERSPECTIVE_ROLES_FILTER}
      - PUBLICATION_LIST_ROLES_FILTER=${PUBLICATION_LIST_ROLES_FILTER}
      - DEFAULT_SELF_SCOPED_ROLES=${DEFAULT_SELF_SCOPED_ROLES}
      - NEXT_PUBLIC_INSTITUTION_NAME=${INSTITUTION_NAME}
      - FIELD_ENC_PRIMARY_KID=${FIELD_ENC_PRIMARY_KID}
      - FIELD_ENC_KEYS_JSON=${FIELD_ENC_KEYS_JSON}
    depends_on:
      crisalid-bus:
        condition: service_healthy
    volumes:
      - ./config:/config:ro
      - ./theme:/custom-theme:ro
    networks:
      - svp-network
      - crisalid-front
    profiles:
      - sovisuplus

networks:
  svp-network:
    driver: bridge
  crisalid-front:
    driver: bridge
services:
  svp-db:
    image: postgres:16
    container_name: svp-db
    restart: unless-stopped
    environment:
      POSTGRES_USER: ${SVP_DB_USER}
      POSTGRES_PASSWORD: ${SVP_DB_PASSWORD}
      POSTGRES_DB: ${SVP_DB_NAME}
    expose:
      - 5432
    ports:
      - 5432:5432

2) Apollo API (docker/apollo/apollo.yaml)

Ensure the public mapping is present (it typically is already because Apollo graphql GUI is one of the main user interfaces):

apollo.yaml
services:
  apollo:
    # Overriden by environment specific YAML configurations (e.g. docker-compose.dev.yaml)
    # image: crisalidesr/crisalid-apollo:latest
    container_name: crisalid-apollo
    restart: unless-stopped
    ports:
      - ${APOLLO_API_PORT}:4000
    depends_on:
      neo4j:
        condition: service_healthy
    environment:
      # Overriden by environment specific YAML configurations (e.g. docker-compose.dev.yaml)
      # - APP_ENV=DEV
      - NEO4J_URI=bolt://${NEO4J_HOST}:${NEO4J_BOLT_PORT}
      - NEO4J_USER=${NEO4J_USER}
      - NEO4J_PASSWORD=${NEO4J_PASSWORD}
      - ENABLE_API_KEYS=${APOLLO_ENABLE_API_KEYS}
      - API_KEYS=${SOVISUPLUS_GRAPHQL_API_KEY}
    networks:
      - ikg-network
      - crisalid-front
    profiles:
      - apollo
networks:
  ikg-network:
    driver: bridge
  crisalid-front:
    driver: bridge
services:
  apollo:
    image: crisalidesr/crisalid-apollo:latest
    ports:
      - ${APOLLO_API_PORT}:4000

Your local SoVisu+ will call http://localhost:${APOLLO_API_PORT} (GraphQL endpoint /graphql).

3) CRISalid Bus / RabbitMQ (docker/crisalid-bus/crisalid-bus.yaml)

Only the management UI port is exposed by default, but you can uncomment the AMQP port to allow external tools to connect:

crisalid-bus.yaml
services:
  crisalid-bus:
    # Overriden by environment specific YAML configurations (e.g. docker-compose.dev.yaml)
    # image: rabbitmq:3-management
    container_name: 'crisalid-bus'
    restart: unless-stopped
    environment:
      - RABBITMQ_DEFAULT_USER=${CRISALID_BUS_USER}
      - RABBITMQ_DEFAULT_PASS=${CRISALID_BUS_PASSWORD}
      - RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS=-rabbit load_definitions "${CRISALID_BUS_DEFINITIONS_FILE}"
    ports:
      - "${CRISALID_BUS_HTTP_PORT}:15672"
    # - "${CRISALID_BUS_AMQP_PORT}:5672" # To connect app under development on host machine or from external server
    expose:
      - "${CRISALID_BUS_AMQP_PORT}"
    volumes:
      - ./rabbitmq-data:/var/lib/rabbitmq
      - ./rabbitmq-logs/:/var/log/rabbitmq
      - ./definitions.json:${CRISALID_BUS_DEFINITIONS_FILE}:ro
    healthcheck:
      test: rabbitmq-diagnostics check_port_connectivity
      interval: 1s
      timeout: 3s
      retries: 30
    networks:
      - crisalid-front
      - crisalid-back
    profiles:
      - crisalid-bus
networks:
  crisalid-front:
    driver: bridge
  crisalid-back:
    driver: bridge
services:
  crisalid-bus:
    image: rabbitmq:3-management
    container_name: 'crisalid-bus'
    environment:
      - RABBITMQ_DEFAULT_USER=${CRISALID_BUS_USER}
      - RABBITMQ_DEFAULT_PASS=${CRISALID_BUS_PASSWORD}
      - RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS=-rabbit load_definitions "${CRISALID_BUS_DEFINITIONS_FILE}"
    ports:
      - "${CRISALID_BUS_HTTP_PORT}:15672"
      - "${CRISALID_BUS_AMQP_PORT}:5672"
    expose:
      - "${CRISALID_BUS_AMQP_PORT}"

🧩 Use the Backend-Only Profile

Replace the sovisuplus profile with sovisuplus-db, which starts the DB and related services but not the SoVisu+ container itself.

Example command:

docker compose \
  --profile cdb \
  --profile neo4j \
  --profile apollo \
  --profile crisalid-bus \
  --profile harvester \
  --profile ikg \
  --profile keycloak \
  --profile sovisuplus-db \
  up --remove-orphans

▢️ Run SoVisu+ Locally

Start your Next.js app on the host as usual (e.g., in the SoVisu+ repo):

npm run dev # for the main web gui
npm run dev:listener # for the backend listener

Make sure your local env points to the Docker services. Typical variables (names vary by project):


NEXT_PUBLIC_SUPPORTED_LOCALES="fr,en"

DATABASE_URL="postgresql://sovisuplus:sovisuplus_password@localhost:5432/sovisuplus?schema=public"

KEYCLOAK_CLIENT_ID="sovisuplus"
KEYCLOAK_CLIENT_SECRET="use-the-same-secret-as-in-docker-compose"
KEYCLOAK_ISSUER="http://keycloak.local:8080/realms/crisalid-inst"
NEXTAUTH_SECRET="use-a-secure-random-secret"

AMQP_USER="crisalid_bus_user"
AMQP_PASSWORD="use-the-same-password-as-in-docker-compose"
AMQP_HOST="localhost"
AMQP_PORT="5672"
AMQP_QUEUE_NAME="sovisuplus"
AMQP_EXCHANGE_NAME="graph"

GRAPHQL_ENDPOINT_ENABLED="true"
GRAPHQL_ENDPOINT_URL="http://localhost:4000/graphql"
GRAPHQL_API_KEY_ENABLED="false"
GRAPHQL_API_KEY="not-needed-in-dev"

PERSPECTIVES_ROLES_FILTER=["author ","author of introduction, etc. ","author of afterword, colophon, etc. ","author in quotations or text abstracts ","editor ","editor of compilation ","translator" ]
PUBLICATION_LIST_ROLES_FILTER=["analyst ","annotator ","author ","author in quotations or text abstracts ","author of afterword, colophon, etc. ","author of introduction, etc. ","cartographer ","commentator for written text ","compiler ","composer ","conceptor ","contributor ","curator ","degree committee member ","dissertant ","donor ","editor ","editor of compilation ","film director ","film editor ","former owner ","illustrator ","interviewee ","interviewer ","opponent ","organizer ","other ","photographer ","praeses ","production personnel ","project director ","publisher director ","rapporteur ","scientific advisor ","software developer ","sound designer ","speaker ","stage manager ","thesis advisor ","translator ","writer of accompanying material"]

ORCID_URL="https://sandbox.orcid.org"
APP_URL="http://sovisuplus.local:3000"
ORCID_SCOPES="/person/update"
ORCID_CLIENT_ID="use-the-same-client-id--provided-by-orcid-as-in-docker-compose"
ORCID_CLIENT_SECRET="use-the-same-client-secret-provided-by-orcid-as-in-docker-compose"

Visit SoVisu+ at:

http://sovisuplus.local:3000