Klish Group is a consulting firm specializing in Web and Digital Content Management strategy, design and implementation

Customer Support
info@klishgroup.com
312.546.4727

OpenText LiveSite Content Services Container

One container that simplifies database management, search configuration and exposes all logging.

Run the OpenText Content API as One Container, Not Three

LiveSite Content Services (LSCS) is the REST API your web sites and applications call to retrieve content, run personalization and targeting rules, and answer search queries against content published from OpenText Web CMS (TeamSite).

OpenText ships their version of LSCS as a Kubernetes deployment built from three images: the runtime itself, a separate init image to create the database schema, and a log rotation image to manage the log files the runtime writes to disk. Klish Group packages the same OpenText release into a single image that applies its own database migrations, uploads its own Solr configuration to ZooKeeper, and streams every log line it produces to stdout as JSON. It runs the same way under Docker Compose on a laptop, on a Swarm node, or on AWS ECS.

Why Teams Ask Us For This Image

One Image, Not Three

Schema migrations and the Solr configset upload happen in the entrypoint. No init container, no rotation sidecar, no orchestrator to sequence them.

Roughly 45% Smaller

0.97 to 1.11 GB across every release we build, against 1.81 to 2.01 GB for the vendor's, on a fully package-managed base rather than a hand-assembled compatibility layer.

Logs You Can Query

Six log sources - Tomcat, the access log, the application, and the three startup phases - all as JSON and available in your log framework.

Three Databases Suppoted

PostgreSQL, MySQL and SQL Server images built in parallel instead of just PostgreSQL supprt in the OpenText provided image.

Side by Side With the Vendor Image

Measured directly against every registry.opentext.com/ts-lscsrt image from 23.4 to 26.2, and every image we build from 23.4.1 to 25.4.1.

  OpenText runtime image Klish Group LSCS image
Runtime platform Built around a Kubernetes deployment Docker, Docker Compose, Docker Swarm, AWS ECS
Images per instance Three - the runtime, an init image for the database schema, and a log rotation image One
Base OS Alpine 3.18 to 3.23 across the 23.4 to 26.2 releases, with a glibc compatibility layer to run a JVM that expects glibc Ubuntu 24.04 LTS, fully package-managed
Image size 1.81 GB to 2.01 GB across the 23.4 to 26.2 releases 0.97 GB to 1.11 GB across the 23.4.1 to 25.4.1 releases
Java and Tomcat patch level Fixed at the date the image was built - 25.4.0 still ships the Java 21.0.3 that 24.4.0 shipped in 2024, and Tomcat 10.1.48 Current at every build - Java 21.0.12 and Tomcat 10.1.59 today, Java 11.0.32 and Tomcat 9.0.122 on the 23.4 line
Supported databases PostgreSQL only - the init image carries only a PostgreSQL driver and PostgreSQL schema scripts PostgreSQL, MySQL and SQL Server, one image per vendor
Database schema A separate ts-runtime-initdb image that has to run first Liquibase, run by the entrypoint and guarded by its own lock, so every replica can start at once
Solr configuration An operator step outside the container Uploaded to ZooKeeper at startup - lock-guarded, and needs no running Solr node
Resource sizing A hard-coded default of -Xms256m -Xmx1024m, unchanged across six releases and two and a half years, with nothing in the image's own configuration derived from the container's CPU or memory limit Heap sized as a percentage of the container limit, a metaspace ceiling, and a bounded worker pool and connection pool - all environment-tunable
Getting logs off the box Tomcat's internal logs, the access log and the application log all written to files under the Tomcat log directory, with a companion ts-logrotate image to keep them from filling the volume Every one of those on stdout as one JSON object per line, collected by whatever log driver you already use
Log retention A rotation image, plus a volume sized to hold what it rotates Nothing is written to disk, so there is nothing to rotate

Six Log Sources, One JSON Shape

A stock LiveSite Content Services container produces log output in four different formats across three different destinations, and the interesting ones are files inside the container. Tomcat's own logging, the HTTP access log and the application log each have their own framework, their own layout and their own rotation settings.

We put all of them - plus the three startup phases, the entrypoint, the schema migration and the configset upload - onto stdout as newline-delimited JSON. Every line opens with the same two fields, ts and stream, so one query selects across the whole container and then narrows by source:

{
  "ts":           "2026-08-13T17:49:09.849Z",
  "stream":       "access",
  "host":         "10.0.42.17",
  "method":       "GET",
  "path":         "/lscs/v1/content/project/corporate",
  "query":        "?format=json",
  "statusCode":   200,
  "size":         18422,
  "elapsedTime":  37,
  "requestHeaders": { "User-Agent": "...", "X-Forwarded-For": "..." }
}

Nothing is written to disk

Tomcat's usual trick for putting an access log on stdout does not work in a container that drops to an unprivileged user - the pipe behind /dev/stdout is owned by root, and reopening it fails. We ship an access log valve that writes through the output stream the JVM already holds, so there is no log directory, no rotation schedule and no volume to size.

One query across every stream

Because the envelope is shared, a single CloudWatch Logs Insights or OpenSearch query can find every 5xx in the access log, the application exception that caused it and the Tomcat record underneath, without a parsing rule per source. Stack traces are escaped, so a multi-line exception is still one parseable line.

The status code, response size and elapsed time are emitted as JSON numbers rather than strings, which is what makes a threshold actually work - Tomcat's own JSON valve quotes every value, and against a quoted field most query languages sort lexicographically, where "9" ranks above "1000". A CloudWatch metric filter on a latency threshold matches numbers only, and would never fire at all.

Levels you can turn up in place

The application, Hibernate, Spring, Liquibase and root log levels are each an environment variable. Raising one to diagnose a problem is a task definition change, not an image rebuild or a file edit inside a running container.

Probes stay out of the log

A health check every thirty seconds is a few thousand access log lines a day of no diagnostic value, billed and indexed like any other. The status endpoint is excluded by default, and the exclusion list is an environment variable - so a load balancer probing a different path is one setting away.

Runs on Common Platforms

Docker & Compose

A ready-made stack with ZooKeeper, Solr and your database, and a compose file per database vendor so you can bring the whole thing up against the one you actually run.

Docker Swarm

A stack file that references the license as an external swarm secret, plus a two-instance clustered example behind nginx for testing how replicas behave on startup.

AWS ECS & Fargate

Task definition and AWS CDK examples, the license pulled from Secrets Manager by ARN, and the health check restated where ECS will actually evaluate it.

Seven things have to be supplied on every platform - the image tag, the licensed hostname, the license, the database, ZooKeeper, the shared content volume and the port. Only the syntax changes between them:

docker run -d --name lscs \
  --hostname lscs01.example.com \
  -p 8080:8080 \
  -v /srv/livesite/data:/data \
  -e DB_URL='jdbc:postgresql://dbhost:5432/lscontent' \
  -e DB_USER=teamsite \
  -e DB_PWD='...' \
  -e ZOOKEEPER_URL='zk1.example.com:2181,zk2.example.com:2181/livesite' \
  -e LSCS_LICENSE="$(cat /path/to/LSCS.lic)" \
  registry.example.com/lscs-container:24.4.0.0-postgres

The database password is encrypted into the application's configuration at startup rather than left in plain text on disk, and the license can arrive as an environment variable, a Docker or Swarm secret, or an AWS Secrets Manager reference.

Sized to the Container It Runs In

Inside the image, OpenText's entire memory policy is a single line setting a hard-coded 1 GB heap ceiling. It has not changed from their November 2023 release through their April 2026 one, and nothing else in the image's configuration is derived from the CPU or memory limit it was given. On the smallest Fargate task this service is commonly run on - half a vCPU and 1 GB - that heap ceiling is the whole container.

Task size Cores the JVM sees Collector Heap, chosen automatically Worker threads OpenText heap / threads
0.5 vCPU / 1 GB1Serial512 MB25 (default)1 GB / 200
1 vCPU / 2 GB1Serial1 GB501 GB / 200
2 vCPU / 4 GB2G12 GB1001 GB / 200

Every row was read out of the JVM itself, constrained to that task size, in twenty of our images - seven releases, all three databases, Java 11 and Java 21 alike - and every one of them chose the same core count, collector, heap and 256 MB metaspace ceiling at each tier. The OpenText column is the same in all six of their published releases.

We then started each of the seven releases on the smallest tier with nothing configured. Each came up healthy and held 38 to 42 MB of live heap after a full collection. Under 50 concurrent clients, memory peaked at 480 MB of the 1 GB limit, and Tomcat stopped at exactly 25 worker threads.

Moving up a tier is one setting. The heap follows the container limit on its own, and the collector follows from the core count - so a latency profile measured at one vCPU does not carry to two, which is worth knowing before you are surprised by it.

A heap that follows the limit

The heap is a percentage of whatever memory the container was given rather than an absolute number, so it is right at every task size without a value per environment. Metaspace is capped at 256 MB against a measured 76 to 94 MB across every release we build.

A failure you can diagnose

A heap ceiling equal to the container limit does not produce a Java error. Measured in a 1 GB container, it is killed by the kernel - exit 137, no message, no stack trace. Sized correctly, the same load throws an OutOfMemoryError you can actually read.

Admission control, not just a queue

The vendor's thread pool is stock, which means 200 concurrent requests on a task the JVM sees as one core. They degrade together - including the health check - so the orchestrator replaces a task that is merely busy. Ours bounds the worker pool and the database pool, and surfaces overload as latency instead.

The vendor's own override hatches do not close this gap: an absolute heap setting always beats a percentage-based one regardless of the order they appear in, so percentage sizing on their image means replacing their line wholesale. The one place resource awareness exists in the product is their Kubernetes operator, and even there it is opt-in: the operator can work out a heap from the pod's memory limit, but only from a percentage someone has written into its configuration. With none written, the bundled sizing profiles raise the LSCS pod's limit as high as 8.4 GB while the heap stays at 1 GB. Docker, Compose, Swarm and ECS have none of it.

Correct Under Real Conditions

The differences that matter are the ones you only find by running the thing until it breaks.

1

A health check that can actually fail

The admin status endpoint sets its HTTP 200 before it checks anything, so a plain curl -f probe passes with the database unreachable and passes with Solr unreachable. Our check parses the response and tests the aggregate status attribute, which is only UP when the database and the search server both are.

2

A grace period sized against a cold start

ECS ignores a health check baked into an image, so we document the command to place in the task definition - with a start period measured against what a first boot really does. A cold start is 105 to 109 seconds on the smallest task size; a replica that loses the configset upload lock waits on it and needs around 225. The 180-second start period plus three retries at thirty seconds covers both, with margin to spare. A probe window sized for a warm container replaces the task before it ever finishes starting.

3

Safe when every replica starts at once

Schema migrations take the Liquibase lock; the Solr configset upload takes an ephemeral ZooKeeper lock that releases itself if the holder dies.

Your Database, Not Only The Reference One

OpenText's Kubernetes images are built for PostgreSQL only: their database initialization finds its JDBC driver by the file name postgresql*.jar and runs setup and upgrade scripts written only for PostgreSQL. A LiveSite installation already running on MySQL or SQL Server would have to rebuild that layer, and keep its upgrade scripts current every release, before it had a container to move to. Ours covers all three.

Built three ways, every build

PostgreSQL, MySQL and SQL Server images are produced in a parallel build matrix. Each carries its own JDBC driver, dialect and connection validation query, and each is scanned and gated independently.

Verified against a live database

Not a schema dry run - the full stack comes up against a real instance of each vendor. Running all three for the first time surfaced four defects in the vendor's changelog that a PostgreSQL-only run could never have shown.

The connection details that trip people up

MySQL 8 needs a public key retrieval flag over a plain connection; SQL Server drivers began defaulting to TLS at version 10 and fail against a self-signed certificate unless told otherwise. Both are documented with working connection strings rather than discovered in a failing deployment.

Supply Chain You Can Hand To Security

Every image is scanned before it is pushed, and the evidence is produced as an artifact rather than as an assertion.

A CycloneDX SBOM per build

Generated from the finished image, archived with every build and published to a dependency tracker. Yours to feed into your own compliance tooling.

A gate, not a report

A critical finding in anything we control fails that vendor's build and the image is never pushed. A readable HTML scan report is published alongside it.

Vendor findings shown, never hidden

The criticals that live inside OpenText's own web application cannot be patched from a Dockerfile, and are present in OpenText's newer images too. We list them in a separate table with the file path that proves why.

Rebuilt monthly

A scheduled build each month rolls the base packages forward, so a release you deployed a year ago can be refreshed against today's operating system patches without a LiveSite version upgrade.

Releases We Package

One image per LiveSite release and database vendor, each built from OpenText's own installer. The Java, Tomcat, Solr and ZooKeeper versions a release needs are resolved from the release itself during the build, so upgrading is a tag change rather than a rebuild of your platform.

Version Java Tomcat Solr ZooKeeper Our image OpenText image
23.4.1119.08.11.23.8.30.97 GBNot published
23.4.2119.08.11.23.8.30.97 GBNot published
24.4.02110.19.5.03.9.21.08 GB1.95 GB
24.4.12110.19.7.03.9.21.08 GBNot published
24.4.22110.19.8.03.9.31.08 GBNot published
25.4.02110.19.8.03.9.41.09 GB2.01 GB
25.4.12110.19.10.13.9.51.11 GBNot published

Image sizes are the same to within 3 MB for the PostgreSQL, MySQL and SQL Server builds of a release. Java and Tomcat are taken from the current patch release of their line at each build rather than pinned, so the monthly rebuild carries their security fixes forward without a change to the release.

The Solr and ZooKeeper columns move per patch release rather than per line, because they follow the search client bundled in that release rather than the version number. Moving from the 23.4 line to 24.4 crosses a Solr major version, so the search index should be rebuilt as part of the upgrade - one API call, but one worth planning for rather than discovering.

You supply the license. These images package OpenText's own installer, so a valid LiveSite license purchased from OpenText is required to run them. What we provide is the packaging, the platform integration and the operational work around it.

What You Get

The image

Pulled from our registry or built in your own pipeline from the repository, so the image your auditors review is the one you produced.

Deployment templates

Compose files, a Swarm stack file, an ECS task definition and AWS CDK snippets - each with the settings that matter already correct.

A local stack to test against

ZooKeeper, Solr, a database admin UI and your database of choice, all in one command, so a configuration change can be proven before it reaches an environment.

New release packaging

When OpenText ships a new LiveSite version, we work through the schema differences, the search configuration changes and the configuration merges, then validate and package it.

Platform help

We can stand the service up in your environment, wire the load balancer, ZooKeeper quorum and log destinations correctly, and deploy the surrounding infrastructure as code.

Someone who knows this product

Klish Group has run OpenText Web CMS (TeamSite), OpenDeploy and LiveSite for enterprise customers for years.

Want to run LiveSite Content Services in a container without standing up Kubernetes?

Request Access