services: neo4j: image: neo4j:5 container_name: agenticcode-neo4j ports: - "7474:7474" - "7687:7687" environment: NEO4J_AUTH: neo4j/agenticcode # Memory caps. Without them the JVM sizes its heap ergonomically at 25% of host RAM # (~7.8 GiB here) and never gives it back: measured 6.17 GiB still held one hour after a # deep refresh of upms had finished, at <1% CPU. The cap is what forces the JVM to # collect rather than commit. # 2G -> 4G (2026-09-23): a deep ingest of upms2 (6311 Natural files) failed at batch 1601-1800 # with "allocation of an extra 34.2 MiB would use more than the limit 1.4 GiB ... # dbms.memory.transaction.total.max threshold reached". That limit defaults to 70% of the heap, # and one 200-file persist transaction now writes ~64k nodes / ~159k relationships (item 160 # positional nodes, copycode copies), which no longer fits in 1.4 GiB. 4G lifts the ceiling to # 2.8 GiB. The idle-heap concern above still holds: G1PeriodicGCInterval returns it. NEO4J_server_memory_heap_max__size: "4G" NEO4J_server_memory_heap_initial__size: "512M" # 512M -> 1G -> 3G -> 2G. The store has grown to 2.4 GB (1.2 GB of it range indexes), so 1G # covered only ~42% of it. 2G covers nearly all of it; the 2.5 GB of transaction logs are # deliberately not counted, Neo4j does not keep them here. Both sizes were measured on a deep # refresh of upms: 1G = 552 s, 3G = 519 s, 2G = 517 s. The whole gain is already had at 2G, so # the third gigabyte stays with the host (a 4 GB VirtualBox guest runs alongside). # # The gain is ~6% and modest on purpose to record why: `directio` is false, so Neo4j reads # through the OS file cache and the store is cached twice. Block I/O across two whole refresh # runs was 237 MB read against a 2.4 GB store — there was never disk I/O to save. A cache miss # costs a read() syscall plus a copy, not a seek, and 6% is what removing those is worth. On a # host whose file cache could NOT hold the store, the same setting would matter far more — so # treat these numbers as this machine's data points, not as a recommendation. # Community Edition cannot pre-warm the cache: the first refresh after a restart runs cold # (540 s here) and is not a fair measurement. NEO4J_server_memory_pagecache_size: "2G" # Return committed-but-unused heap to the OS while idle instead of sitting on it. Without this # the JVM held everything it had ever needed: 6.17 GiB an hour after a refresh had finished. NEO4J_server_jvm_additional: "-XX:G1PeriodicGCInterval=300000" # Hard ceiling: heap 2G + pagecache 2G + metaspace, direct buffers and GC overhead. # Raised 4g -> 5g after a verification run peaked at exactly 4096 of 4096 MB — no OOM-kill, but # zero reserve. An OOM-kill mid-refresh leaves the graph half-updated, which is far worse than a # spare GB. This follows the page cache: Neo4j's own rule of thumb is heap + page cache + ~1G, so # 2G of cache means 6g here — 1G of reserve, not generosity. Do not raise the page cache without # raising this too. 6g -> 8g with the heap raise to 4G (same rule: 4G heap + 2G cache + reserve). mem_limit: 8g volumes: - neo4j-data:/data healthcheck: test: [ "CMD-SHELL", "cypher-shell -u neo4j -p agenticcode 'RETURN 1' || exit 1" ] interval: 5s timeout: 5s retries: 20 start_period: 30s ac-code-server: build: # Repository root, not ./ac-code-server: the image also carries the TypeScript sidecar from # ac-parser-typescript/sidecar (item 192). The root .dockerignore limits what is sent. context: . dockerfile: ac-code-server/src/main/docker/Dockerfile.jvm container_name: agenticcode-server ports: - "8787:8787" environment: NEO4J_URI: bolt://neo4j:7687 NEO4J_USER: neo4j NEO4J_PASSWORD: agenticcode # JDK_JAVA_OPTIONS (not JAVA_OPTS) — the entrypoint is `java -jar`, which expands no shell # variable; the JVM reads this one by itself. Both settings must live here: a value set in # compose replaces the image's, it does not append, so the log-manager property would be # silently dropped if it were left in the Dockerfile. # # -Xmx2500m. Ergonomics would allow 7956 MB, and the process then kept 4.91 GiB resident an # hour after a refresh had ended. Sizing history, because the first cap was too tight: a deep # *refresh* peaked at 1593 MB, which suggested 2 GB — but a `project recreate --deep` (heavier: # ingests from empty) then peaked at 1924 MB, i.e. 94% of that cap. 2500m restores ~25% headroom # while keeping nearly all of the reduction. # G1PeriodicGCInterval returns committed-but-unused heap to the OS while idle. JDK_JAVA_OPTIONS: >- -Xmx2500m -XX:G1PeriodicGCInterval=300000 -Djava.util.logging.manager=org.jboss.logmanager.LogManager # Heap 2.5G + metaspace/code cache/direct buffers. Measured RSS peak was 3472 MB against a 3 GB # heap cap, i.e. ~1.9 GB non-heap. Item 192 adds the TypeScript sidecar, a Node process the JVM # runs per npm workspace during a deep pass: measured ~0.75 GB RSS per workspace, capped at # --max-old-space-size=1024 and run before the JVM's own persist/enrichment peak — so 4g would # have been tight only if the two peaks coincided; 5g leaves room for that case. mem_limit: 5g volumes: # Mounted at the same absolute host path so project roots registered via # the API (which store absolute host paths) resolve inside the container too. # One entry per registered project root (GET /api/projects) — add a line # here whenever a new project root is registered outside this repo. - /home/ingo/deve/agenticCode:/home/ingo/deve/agenticCode:ro - /home/ingo/deve/uniqa/uniqa-upms-app:/home/ingo/deve/uniqa/uniqa-upms-app:ro - /home/ingo/deve/uniqa/pur-sources/backend:/home/ingo/deve/uniqa/pur-sources/backend:ro # Item 192: the pur frontend (project `purfe`, language typescript). Read-only like the others; # the sidecar runs with noEmit and reads the frontend's own node_modules for library typings. - /home/ingo/deve/uniqa/pur-sources/frontend:/home/ingo/deve/uniqa/pur-sources/frontend:ro - /home/ingo/deve/tools/conqat/system/src/250401_UMPS/src/pur-analysis/pur-legacy:/home/ingo/deve/tools/conqat/system/src/250401_UMPS/src/pur-analysis/pur-legacy:ro depends_on: neo4j: condition: service_healthy restart: unless-stopped ac-ui: build: context: ./ac-ui dockerfile: Dockerfile container_name: agenticcode-ui ports: - "5174:80" depends_on: - ac-code-server restart: unless-stopped volumes: neo4j-data: