PowerSolver Docs
Back to Wizard
This page belongs to the full documentation. You don't need it to plan: back to the essentials.

PowerSolver 2.0 — Administrator Guide

How to deploy, configure, and operate PowerSolver in production. Aimed at sysadmins and DevOps.

PowerSolver 2.0 is live. It runs on the same hostnames, ports, container names and redis sidecar the 1.x stack used.

This guide is the steady-state reference. The cutover, burn-in and 1.x archive procedures are complete and archived under _graveyard\PowerSolver\documentation\.


1. Architecture

┌─────────────────────────────────────────────────────────────────┐
│  PowerSolver 2.0 stack (per-host)                                │
│                                                                  │
│  ┌──────────────────┐    ┌──────────────────┐                   │
│  │ powersolver-api  │◄───┤ powersolver-redis│                   │
│  │  :8001           │    │  :6379           │                   │
│  │  Spring Boot 4   │    │  redis:7-alpine  │                   │
│  │  Timefold 2.0.0  │    │  audit / history │                   │
│  │  Java 21         │    └──────────────────┘                   │
│  └────────┬─────────┘                                            │
│           │                                                      │
│  ┌────────▼─────────┐                                            │
│  │ powersolver-     │                                            │
│  │   wizard         │                                            │
│  │  :8080           │                                            │
│  │  nginx:alpine    │                                            │
│  │  + static HTML   │                                            │
│  └──────────────────┘                                            │
└─────────────────────────────────────────────────────────────────┘

Support products (separate compose stacks, joined via pdp-network):
  poweradmin-api      :8010   — wizard fields, system config
  powerai-api         :8060   — LLM-driven spec generation
  powerorchestrator   :8050   — PDP pipeline orchestration

Reverse proxy (nginx) terminates TLS and routes:

Public hostname→ containerNotes
api.planningpowertools.compowersolver-api:8001Solver REST surface
wizard.planningpowertools.compowersolver-wizard:80 (host port 8080)Static UI
admin-api.planningpowertools.compoweradmin-api:8010Unchanged from 1.x
powerai-api.planningpowertools.compowerai-api:8060Unchanged
orchestrator-api.planningpowertools.compower-orchestrator:8050Unchanged

2. Container layout

FilePurpose
powersolver-api/docker-compose.ymlSolver + redis sidecar
powersolver-api/DockerfileTwo-stage build: Maven JDK 21 → JRE 21 runtime
powersolver-wizard/docker-compose.ymlnginx:alpine static container
powersolver-wizard/DockerfileCopies static assets into the image

Or alternatively, use the centralised deployment toolkit at D:\PowerProjects\documentation\installation\ (preferred for production — handles image transfer, network creation, db backup):

AssetPurpose
project-registry.jsonBuild context + ports + redis for every product
server-compose/docker-compose-powersolver.ymlProduction compose (no build: block; loads pre-built image)
build-and-export.ps1Local build → .tar
transfer-images.ps1SCP .tar to server
deploy-remote.shServer-side load + restart + DB backup
deploy-all.ps1Orchestrates the full chain

server-compose/docker-compose-powersolver.yml

services:
  powersolver-api:
    image: powersolver-api:latest
    container_name: powersolver-api
    ports:
      - "8001:8001"
    environment:
      - JAVA_OPTS=-Xmx2048m -Xms512m
    restart: unless-stopped
    depends_on: [redis]
    networks: [pdp-network]

  redis:
    image: redis:7-alpine
    container_name: powersolver-redis
    ports: ["6379:6379"]
    volumes: ["powersolver-redis-data:/data"]
    restart: unless-stopped
    networks: [pdp-network]

volumes:
  powersolver-redis-data:
    name: powersolver-redis-data

networks:
  pdp-network:
    external: true

3. Environment variables

powersolver-api:

VariableDefaultMeaning
JAVA_OPTS-Xmx2048m -Xms512mJVM heap. Container-aware — does NOT need -XX:+UseContainerSupport (JDK 21 default).
JAVA_TOOL_OPTIONSunsetAlternative way to set JVM flags; useful for -XX:MaxRAMPercentage=75.
SERVER_PORT8001Spring's HTTP listen port. Should stay 8001 (matches docker-compose port mapping).
POWERSOLVER_ORCHESTRATOR_URLunsetIf set, emits solve-completion events to PowerOrchestrator. Empty = standalone.

powersolver-wizard: no env vars (pure static). The wizard reads its runtime config from PowerAdmin's /api/system-config endpoint — see Wizard configuration below.


4. Redis sidecar

powersolver-redis runs alongside the solver, exposes 6379, persists data to the named volume powersolver-redis-data.

The 2.0 solver does NOT use redis for job state (it's synchronous — no async-job machinery). Redis is kept for:

  • Job history / audit — operators can write each completed solve to redis for retention (current implementation: not wired, but the volume is in place).
  • Future async-job machinery — if a later release reintroduces a queue, the sidecar is ready.
  • Parity with 1.x operability — the 1.x ops surface relied on this redis for job-status inspection; keeping it preserves muscle memory.

Don't remove it without explicit sign-off. Removing it would silently delete the historical job data the volume holds.

Inspecting the volume

docker run --rm -it -v powersolver-redis-data:/data redis:7-alpine redis-cli -p 6379 KEYS '*'
docker run --rm -it -v powersolver-redis-data:/data redis:7-alpine redis-cli -p 6379 GET '<key>'

Backup

The redis volume is automatically preserved across docker compose down / up -d (a named volume; down doesn't touch it). For external backup, snapshot the volume:

docker run --rm -v powersolver-redis-data:/data -v $(pwd):/backup alpine \
  tar czf /backup/redis-backup-$(date +%F).tgz -C /data .

5. Wizard configuration

The wizard reads its config from PowerAdmin (https://admin-api.planningpowertools.com/api/system-config). Critical keys:

KeyDefaultPurpose
system.solver.api_base_urlhttps://api.planningpowertools.comWhere the wizard POSTs solves.
system.wizard.max_entities1000Soft cap on Step 2 row count.
system.wizard.max_planning_variables4Max variables in Step 3.
system.wizard.max_values_per_variable500Max value-range items per variable.
system.wizard.max_constraints100Max Step-4 entries.
system.wizard.default_solver_timeout120Step 6 default.

The wizard's ConfigService falls back to hardcoded defaults if PowerAdmin is unreachable. To change a production setting, update the row in PowerAdmin's system_config table and restart the wizard container.


6. Network architecture

All PowerSolver-stack containers join the shared pdp-network Docker network. This lets PowerOrchestrator reach http://powersolver-api:8001/api/solve by container DNS — no hostname needed inside the cluster.

The deploy-remote.sh script creates pdp-network automatically before any container starts. If you bring up containers manually:

docker network create pdp-network   # one-time
docker compose up -d                # joins the existing network

7. Logs

powersolver-api logs to stdout (default Spring Boot). Aggregate via:

docker logs -f powersolver-api
journalctl -u docker -f                          # if docker-compose runs as a systemd unit
docker compose logs -f --tail=100 powersolver-api

Useful log levels are pre-configured in application.yml:

LoggerLevel
rootINFO
ai.timefoldINFO (solver progress lines)
com.powersolverDEBUG (our own classes)

To raise/lower verbosity at runtime, set LOGGING_LEVEL_<package> env vars and restart the container.


8. Health and monitoring

EndpointReturns
GET /api/health{"status":"UP","version":"2.0.0","timestamp":"…"}
GET /api/versionengine version, Timefold version, build time — check it after every deployment

The other endpoints (POST /api/validate, /api/check, /api/solve) are described in the API Reference. /api/check ("Can this work?") never solves, except its optional quick plan (?trialSeconds=N, at most 60 s), which counts as a solve for the concurrency limit below.

The container's HEALTHCHECK directive calls /api/health every 30 seconds. docker ps shows (healthy) next to the container name when the last 3 probes succeeded.

For HTTP-level monitoring, point your uptime tool at https://api.planningpowertools.com/api/health (expect HTTP 200 + "status":"UP").

For ongoing workload monitoring, the rubric that carried the launch is still the right one to watch: container restarts, 5xx rate, p95 latency, corpus pass-rate, perf budget, quality gate, and P1/P2 incident count.


9. Common operations

Restart the solver

cd /opt/PowerProjects/PowerSolver/powersolver-api
docker compose restart powersolver-api
# OR full recreate (picks up new image after rebuild):
docker compose build && docker compose up -d

Restart with new code

cd /opt/PowerProjects/PowerSolver/powersolver-api
git pull
docker compose build
docker compose up -d
# Redis is unaffected; in-flight requests get HTTP 502 briefly.

Roll back

If a deployment misbehaves within the rollback window, git checkout the previous tag and rebuild:

cd /opt/PowerProjects/PowerSolver/powersolver-api
git checkout <previous-tag>
docker compose build
docker compose up -d

Redis volume persists across this — historical audit data unaffected.

Inspect the schema-hash cache

PowerSolver caches generated domain classes by schema hash. Cache evictions only happen on container restart. To force a cache flush:

docker compose restart powersolver-api

10. Limits and performance tuning

Limits (powersolver-api/src/main/resources/application.yml, powersolver.limits.*; overridable by environment, e.g. POWERSOLVER_LIMITS_MAXCONCURRENTSOLVES=6 — Spring drops the dashes):

LimitDefaultBeyond it
max-items5,000 per collection400 TOO_LARGE
max-timeout-seconds600400 BAD_CONFIG
max-concurrent-solves4429 BUSY, Retry-After: 30 (the wizard says "The planner is busy with other plans")

A solve answers on the same HTTP request: the reverse proxy must allow at least the maximum time limit (proxy_read_timeout 660s, see installation/HETZNER_GUIDE.md). A solve runs on one thread (no moveThreadCount is set): size the server for max-concurrent-solves cores plus one.

SymptomLever
Solver fails to find feasibility on large problemsIncrease config.timeoutSeconds in the request JSON.
OOM / GC pressureRaise JAVA_OPTS=-Xmx. Default 2GB; the perf reference (50 tasks × 3 hosts) runs comfortably under that.
Slow move-eval throughputThe supplier-shadow hot loop is reflection-cached per Class. If you see ~500 mv/s on simple problems, redeploy — that's the pre-cache regime indicating a bad build. After cache (current main), expect 1500–2600 mv/s on small problems, ~650 mv/s on the 50-task stress case.
First request takes 5–10s while later ones are fastFirst-time per-schema class generation (~3–5s) + JIT warm-up. Subsequent same-schema requests hit the process-global cache.

11. Integration with sister products

PowerAdmin

Provides sign-in (users, tenant), the wizard's form fields per problem type (wizard_fields, by time_grain assignment or sequence, including time-slot fields) and system config.

PowerAI

Generates problem JSON from the wizard's plain-language intake (step 0) and gives advice; it never computes the plan.

PowerOrchestrator

Runs pipelines with a planning stage. Before solving it calls /api/check (falls back to /api/validate on an older engine): an impossible verdict stops the pipeline with the reasons; "at risk" is recorded as an audit event. Its UI shows the Results page and the same messages as the wizard: the shared files and texts are copied by powersolver-wizard/tools/sync-shared.mjs (including powerorchestrator-api/app/engine/solver_feedback_en.json), so deploy PowerSolver and PowerOrchestrator together after a change to those files.

PowerTesting

End-to-end test framework. Health checks, contract conformance, network connectivity, full pipeline runs. See D:\PowerProjects\PowerTesting\.


12. Troubleshooting

Container won't start

docker logs powersolver-api

Common causes:

  • Port 8001 already bound. Check netstat -ano | findstr :8001.
  • JVM OOM at startup. Raise JAVA_OPTS=-Xmx2048m (default).
  • Redis sidecar isn't reachable. Container starts but health checks fail. docker ps for powersolver-redis.

/api/health returns 503

The Spring Boot context started but a sub-component is unhealthy. docker logs will show which one (typically: cannot resolve redis hostname powersolver-redis — fix by ensuring both containers are on pdp-network).

/api/solve returns 400

The problem was refused by validation: the body lists each issue with a code, a path and a message. The wizard shows them in plain words with the step to correct. Frequent causes: a rule names a property no item has, a value of the wrong kind, an unknown id, a list above 5,000 rows, a time limit above 600 s. A derivedShadow expression references X with no known root prefix comes from a caller that drops this. prefixes.

/api/solve returns 429

All solve slots are busy (max-concurrent-solves). Retry after 30 s, or raise the limit if the server has the cores.

/api/solve returns 500

Solver internal error. Capture the request JSON + the trace field of the response into an incident report. Likely root causes:

  • ByteBuddy class generation failure (rare; logged with stack trace).
  • Solver hit a Timefold edge case. Usually fixable by raising or lowering timeoutSeconds or simplifying the problem.

Slow first request after deploy

Expected — DomainAssembler is generating + caching the domain classes for this schema. Subsequent requests with the same schema are fast.


13. Where to go next