On-Premise SAP Commerce Infrastructure: The Complete Hardening and Tuning Guide
For the estates still self-hosting: cluster topology, load balancer stickiness traps including the F5 OneConnect failure mode, Apache tuning formulas, JVM and G1 settings that are current for JDK 17 and 21, database ground rules, and the security hardening list that pen testers check first.
Plenty of SAP Commerce still runs on self-managed infrastructure, and every one of those estates owns the entire stack that CCv2 customers pay SAP to worry about: web servers, load balancers, JVMs, database, patching, and the 3 a.m. consequences of getting any of it wrong. This guide consolidates the deep CX Works infrastructure material into a current reference, with the JVM and Java guidance updated for the 2211 era. One strategic note up front: with on-premise 2205's mainstream maintenance ending in July 2026, every self-hosted estate should be running 2211 update releases or actively planning its path (the upgrade and migration guides cover both directions, staying on-prem or moving to CCv2). The tuning below assumes you are staying current.
Topology Ground Rules#
- Separate storefront and backoffice at every tier: distinct application server groups, distinct web server routing, ideally distinct virtual hosts. Backoffice nodes run sync jobs, imports, and cockpit sessions with resource profiles and SLAs that have nothing in common with storefront traffic. Even the smallest estate gets one dedicated backoffice/batch node; larger ones split batch from cockpit serving.
- Solr never shares fate with the storefront. Standalone mode: a primary dedicated to indexing that serves no queries, plus at least two replicas for search traffic. SolrCloud: ZooKeeper handles leadership. Either way, index load must not steal search latency (the Solr infrastructure guide goes deeper).
- Redundancy at every layer, and a diagram that matches reality in the runbook.
- Media in a cluster needs a sharing strategy: NFS mounted as the platform's media folders is the boring, correct default; rsync from an authoring node only works with a single author and tolerance for lag; object storage backends are supported alternatives.
- One cluster, one database. Active-active databases across two data centers is explicitly unsupported: cache invalidation messages offer no ordering guarantee against async replication, and sync replication buys you cross-DC commit latency instead. DR is a standby database plus a cold standby environment and a DNS switch, rehearsed before launch, not improvised during one.
Load Balancer: Where Stickiness Goes to Die#
Sessions are sticky in SAP Commerce, and the LB tier is where stickiness silently breaks:
- Standard shape: LB in front of web servers,
mod_proxy_balancerfrom web to app tier with sticky sessions on the inner hop. Stick at one layer, not two; double-stickiness produces uneven node load that looks like a capacity problem. - The F5 OneConnect trap, preserved here because it costs real teams real weeks: without a OneConnect profile, the BIG-IP reuses backend TCP connections across client requests and routes by connection, ignoring the session cookie. Symptoms are users randomly losing carts behind corporate firewalls (many users, one TCP connection) and Akamai edge nodes pinning to one backend. Any topology that puts an F5 in front of Tomcat needs OneConnect enabled. Equivalent connection-reuse behavior exists in other LBs; test stickiness through the full chain including the CDN, not from your laptop.
- SSL terminates at the LB (or CDN), never at Tomcat: crypto offload, one certificate location, less app-tier load. Set
X-Forwarded-Proto: httpsat the terminating layer and configure Tomcat'sRemoteIpValveinserver.xmlto interpret it; if your internal addressing is in private ranges the valve'sinternalProxies/trustedProxiesmust include the LB's address or secure-page detection breaks in ways QA rarely catches. - Health checks must not manufacture sessions. An out-of-band health check hitting a JSP creates an HTTP session per probe; expose a minimal filter-level ping URL above the session machinery. Watch HTTP versus Jalo session counts (JMX exposes both); a persistent gap usually means monitoring traffic is inflating one side.
- Block backoffice paths at the LB and the web tier; defense in depth is cheap here and host-header spoofing tests are part of validating it.
Web Tier: Apache, Tuned and Hardened#
Two or three web servers cover even large app clusters. More web servers can actually hurt: each one lists app servers in the same order, so first-hit traffic piles onto the same backends; if you must scale the web tier, rotate the BalancerMember order per web server.
Tuning that carries the load:
- MPM event (or worker) for B2C traffic; prefork wastes the tier. Coordinate threads with the app tier:
MaxRequestWorkers = (tomcat nodes / apache nodes) x max tomcat threadsas the starting formula, few processes with high thread counts, and enough spread betweenMinSpareThreads/MaxSpareThreadsthat Apache is not churning children. - AJP between web and app tier historically outperforms HTTP proxying for this platform; keep the classic blocking AJP connector unless you have measured reasons otherwise, set
URIEncoding="UTF-8", and match Tomcat'sconnectionTimeoutto the BalancerMember TTL. - A dummy first virtual host on every port Apache listens on, so requests with garbage host headers die at a stub instead of reaching Tomcat. Pair with LB rules dropping invalid host headers, and CDN-origin IP restrictions if an edge cache fronts you.
- Know the mod_proxy error-state behavior: with
ProxyErrorOverride on, older Apache versions (below 2.4.13) pull a backend out of rotation for theretryinterval after a single 500. The durable fix is application code that handles its exceptions instead of bubbling 500s to the container; the infrastructure fix is a current Apache. - Serve 503/504/403 pages from the web tier; leave 500/404 to the application. Compression on, static assets served or CDN-cached with proper max-age, maintenance pages switchable in one move and SEO-safe (503 plus Retry-After).
Hardening is a checklist, not a philosophy: no directory listing or server signature, minimal modules, non-root user with locked account and invalid shell, root-owned config with 755-or-tighter permissions, no default CGI or manuals, TraceEnable off, method limits (GET/HEAD/POST/OPTIONS, plus PUT/DELETE only where REST endpoints live), .ht* access denied, protected server-status/balancer-manager, FIPS-grade ciphers only, insecure renegotiation and SSL compression off, RequestReadTimeout plus conservative Timeout/KeepAliveTimeout as DoS mitigation, error logs at notice with 13-plus weeks retention off the root partition. A third-party penetration test before go-live checks exactly this list; run it yourself first.
Application Servers and the JVM#
OS-level: application and logs on a non-root partition, nofile/nproc limits at 65536, unneeded services off, low-latency NICs, and the JGroups UDP kernel buffers (net.core.rmem_max, net.core.wmem_max) raised when startup logs ask for it. Cluster communication is JGroups (UDP multicast by default; TCP where multicast is banned, noting the TCP mode's constraint that the datasource must live in local.properties, not JNDI). Add -Djava.net.preferIPv4Stack=true unless you have a real IPv6 plan, and verify cluster membership in hAC after every topology change.
Sizing starting points that still hold: 4 vCPU nodes with 6 to 8 GB heap and 10 to 14 GB physical RAM, moving to 8-core nodes only to shrink very large clusters. -Xms equals -Xmx, always.
Java, updated for the current platform: 2211 runs on JDK 17 and, per the mandatory framework update track, JDK 21; SapMachine is the JDK SAP ships and supports (check the third-party compatibility matrix for your exact patch release; the old "Oracle JDK only" rule belongs to a previous decade). G1 has been the default collector since JDK 9, so the historical "switch to G1" advice is now "do not switch away from it": no CMS (removed), no serial or parallel collectors for these workloads. GC logging moved to unified logging; replace the old PrintGC flags with -Xlog:gc*:file=/var/log/hybris/gc.log:time,uptime:filecount=10,filesize=50m and keep it enabled in production. Validate GC health under load and soak tests; scheduled restarts to paper over memory growth are a defect report, not an operating procedure. Heap histograms via jmap when in doubt.
Platform configuration essentials: production licenses in place (total region cache entries above 170000 requires the high-performance license); region cache sizes and eviction tuned then re-tested; node warm-up before a fresh node takes traffic (type system preload, top pages, top search queries, or a ramped balancer load factor); logging at WARN with rotation matching retention policy; unused tenants removed above dev; system.unlocking.disabled=true to lock initialization; tenant.restart.on.connection.error=false; cleanup cronjobs for job logs and stale carts (the data maintenance guide's list); secondary caches (price, promotion) verified to invalidate after syncs; hAC Linpack and SQL benchmarks compared across environments (composite above ~1100 per CPU, SQL averages under 0.7 ms) to catch infrastructure drift.
Security Beyond the Web Tier#
The platform-level items that show up in every serious audit: no default cockpit/hMC passwords (empty the *.default.login/password properties on legacy versions); a FlexibleSearch sweep for weak password encodings ({passwordEncoding} not in ('sha256','sha512','pbkdf2'), remembering current platforms default to strong encodings); default accounts reset via ImpEx and re-checked after every system update; media.default.storage.location.hash.salt changed before media exists; the Transparent Attribute Encryption primary password changed before the first user is created (it cannot be rotated later, only its keys can); CORS locked down; HttpOnly cookies verified; anonymous login disabled confirmation; cockpit access restricted to employee groups; secure media folders configured; VJDBC and every unneeded web module removed (check webroots in hAC, and never "disable" Backoffice extensions via webroot tricks, which breaks hAC clusterwide); JMX authenticated with password and access files.
Database#
A qualified DBA is line one, not a luxury: index review beyond the generated ones, tablespace monitoring with auto-extend or alerts, nightly backups per vendor practice, a restore exercise executed and documented before launch, and alerting on blocking sessions. Platform-specific ground rules: case-insensitive collation at creation time; connection pool defaults left alone below 8 cores per node, with pool exhaustion treated as an application bug (missing indexes, unreleased connections, monster queries from bad modeling) rather than a reason to raise limits; no JNDI datasource (recovery behavior after connection loss is worse); no direct SQL data manipulation, ever, because the cache layer you bypass will disagree with the database you edited; test data purged after load tests; JDBC driver versions verified against the compatibility matrix; and if a firewall inspects SQL traffic between tiers, size its CPU for Black Friday, because packet inspection at peak has taken down more than one site whose application tiers were healthy.
The Standing Rhythm#
Monthly OS patch cycles rehearsed during the build phase so the process exists before production does; a pen test by an external party against the release candidate; centralized logging (OpenSearch/ELK or equivalent) with per-tier retention; NIC saturation and packet collision checks in the monitoring baseline; 10 Gb networking between tiers where budgets allow. None of this is glamorous, which is precisely the point: on-premise estates succeed on rhythm and checklists, and every item above earned its place by taking a production site down somewhere.