4.9. S2I Environment Variables
When to Look at This
- When you need to add an environment variable to a build or a deployment
- When a value you set has no effect and you are looking for why
- When you want the list of values you can set
For how to create a build, see 402 Building in the Console; for deployment, see 408 Deployment Strategies. This chapter is the reference for which values you can set.
The S2I Example Created by the Installation (openmaru-war-example)
The installation creates one working S2I example. The intent is that you start from something already running and adapt it, rather than writing a BuildConfig from scratch.
| Item | Value |
|---|---|
| Name | openmaru-war-example |
| Namespace | The same namespace as the eGovFrame sample (egov_project_name) |
| Source | The openmaru-war-example repository the installation created in GitLab (main branch) |
| Builder image | tomcat9-jdk8-ubi8-s2i-openmaru:9.0 |
| Access secret | openmaru-git |
Being a WAR example, it uses the Tomcat 9 + JDK 8 builder. The build output goes to a separate Harbor project so it does not mix with the platform's own images.
A skeleton Deployment is created with it. It points at the image the build produces, so the whole path from build to deployment is visible at once.
# Check the BuildConfig and the skeleton Deployment
kubectl get buildconfig,deployment -n <eGovFrame namespace> | grep openmaru-war-example
The same objects appear on the Builds screen of the COP Console. To switch to your own application, change only the repository address and the builder image.
Custom Build Scripts
# .s2i/bin/assemble
#!/bin/bash
/usr/libexec/s2i/assemble
echo "Running custom build steps..."
npm run build
npm prune --production
# .s2i/bin/run
#!/bin/bash
export NODE_ENV=production
exec npm run start:prod
Where to Put an Environment Variable
An environment variable for an S2I (Source-to-Image) builder image has three possible places, and each applies at a different time. Choosing the wrong one means the value is ignored without any error, so start by telling them apart.
| Place | Applies | Where you put it | Typical variables |
|---|---|---|---|
| 1. Build | Only while the source is turned into an image | BuildConfig spec.strategy.sourceStrategy.env, the environment variable rows of the Console's [Build settings] form, s2i build -e | MAVEN_MIRROR_URL, MAVEN_ARGS_APPEND, S2I_SOURCE_DEPLOYMENTS_FILTER |
2. .s2i/environment | Both build and runtime | The standard file kept in the source repository | Values needed by both build and run |
| 3. Runtime | When the container starts | Deployment spec.template.spec.containers[].env, the environment variables on the Console's deployment screen | TOMCAT_*, JWS_HTTPS_*, JWS_REALM_*, RESOURCES, CATALINA_OPTS_APPEND |
The wrong place is ignored without an error. Putting the runtime variable
TOMCAT_MAX_THREADSinto the build (1) leaves the build successful, the value discarded, and the running container unchanged. Putting the build variableMAVEN_MIRROR_URLinto the runtime (3) likewise leaves the build unaffected. Check the Applies column of each table below.
A build variable, in a BuildConfig:
spec:
strategy:
sourceStrategy:
env:
- name: MAVEN_MIRROR_URL
value: "https://nexus.<domain>/repository/maven-public/"
A runtime variable, in a Deployment:
spec:
template:
spec:
containers:
- name: app
env:
- name: TOMCAT_MAX_THREADS
value: "300"
In the Console, build variables go in the environment variable rows of the [Build settings] form and runtime variables go in the environment variables on the deployment screen (for the procedure, see 402 Building in the Console).
OpenJDK/Tomcat Common (JVM, garbage collection, build tools, proxy)
Informational variables (read-only):
| Variable | Value | Description |
|---|---|---|
JAVA_HOME | /usr/lib/jvm/java-{version} | The JDK installation directory |
JAVA_VERSION | 1.8.0, 11, 17, 21 | The OpenJDK version |
JAVA_VENDOR | openjdk | The Java distribution type |
HOME | /home/jboss (UBI8 family), /home/default (UBI9 family) | The default user's home directory |
USER | jboss (UBI8 family), default (UBI9 family) | The container user. The name differs by family, but the UID is 185 in both |
JAVA_DATA_DIR | /deployments/data | The application data directory |
MAVEN_VERSION | 3.8 (UBI8 family), 3.9 (UBI9 family) | The bundled Maven version |
GRADLE_VERSION | 8.14.5 (UBI8 family), 9.7.1 (UBI9 family) | The bundled Gradle version |
JVM memory and GC settings:
| Variable | Default | Description |
|---|---|---|
JAVA_MAX_MEM_RATIO | 70 (50 on Tomcat images) | Maximum heap as a percentage of available memory. 0 leaves the heap size unset and defers to the JVM default. The default differs by image family — see below |
JAVA_INITIAL_MEM_RATIO | - | Initial heap allocation ratio (%) |
JAVA_UNBOUNDED_HEAP | 4096m | The heap size to use when the container has no memory limit. See below |
GC_CONTAINER_OPTIONS | Chosen automatically (see below) | The garbage collector. Setting a value overrides the automatic choice |
GC_MIN_HEAP_FREE_RATIO | 10 | Minimum percentage of free space to keep after a collection. See below |
GC_MAX_HEAP_FREE_RATIO | 20 | Maximum percentage of free space to keep after a collection. See below |
GC_TIME_RATIO | 4 | The inverse of the time that may be spent in garbage collection. See below |
GC_ADAPTIVE_SIZE_POLICY_WEIGHT | 90 | Weight given to recent observations when resizing the heap (%). See below |
GC_METASPACE_SIZE | 20 | Initial metaspace size (MB) |
GC_MAX_METASPACE_SIZE | 100 | Maximum metaspace size (MB) |
ENABLE_GC_LOG | false | true writes garbage collection logs to standard output. Not applied on JDK 8 images |
ENABLE_HEAP_DUMP | false | true writes a heap dump on an out-of-memory error |
HEAP_DUMP_PATH | /deployments/data | The directory the heap dump is written to |
JAVA_MAX_MEM_RATIO is a ratio, so it needs a memory size to apply to. With a pod memory limit
(resources.limits.memory), that limit is the base and the heap stays inside it.
Without a limit, the base becomes the node's total memory. On a 31 GB node, 70 percent sets a
maximum heap of roughly 22 GB, so one pod reserves most of the node's memory.
JAVA_UNBOUNDED_HEAP applies only in this case and fixes the heap at a set size (4096 MB by
default, with the initial and maximum heap equal). With a pod memory limit, this value is unused and
the ratio applies.
Set a memory limit.
JAVA_UNBOUNDED_HEAPis a safety net for when the limit was forgotten; it does not replace one.
The default heap ratio differs by image family.
| Image family | JAVA_MAX_MEM_RATIO default | Maximum heap in a 2 GB pod |
|---|---|---|
OpenJDK (openjdk*-s2i) | 70 | About 1.4 GB |
Tomcat (tomcat9-*-s2i) | 50 | About 1 GB |
The heap uses only part of the container's memory. The rest goes to metaspace, code cache, thread stacks, and direct buffers outside the heap, and on a web application with many threads that area exceeds 200 MB. The Tomcat images default lower because an application server uses especially much memory outside the heap.
In pods of 1 GB or less, even the default can leave the non-heap area tight. If the application is terminated for running out of memory, raise the pod memory or lower
JAVA_MAX_MEM_RATIOfurther.
If the heap is smaller than expected, check this default first. With 2 GB allocated, a heap of 1.4 GB on an OpenJDK image and 1 GB on a Tomcat image is correct behavior. To change it, set
JAVA_MAX_MEM_RATIOexplicitly.
Thread stack size (-Xss) is not set and uses the JVM default of 1024 KB. To change it, add -Xss
to JAVA_OPTS_APPEND (CATALINA_OPTS_APPEND on Tomcat images).
Automatic garbage collector selection
The garbage collector is chosen from the memory and CPU allocated to the pod. Setting a value in
GC_CONTAINER_OPTIONS overrides the automatic choice.
| Condition | Collector applied |
|---|---|
| Memory limit of 1792 MiB or more and 2 or more CPU cores (JDK 11 and later) | The JVM decides for itself. It usually picks G1 |
| Otherwise | Parallel |
| JDK 8 images | Parallel, regardless of the condition |
The 1792 MiB and 2-core thresholds are the same values the JVM uses to identify server-class hardware. Below them the JVM picks Serial, and Parallel is the better choice, so it is set explicitly. Above them the JVM's own judgment is more accurate, so it is left alone.
JDK 8 is excluded because that version of the JVM does not select G1 automatically.
Heap sizing variables (GC_MIN_HEAP_FREE_RATIO, GC_MAX_HEAP_FREE_RATIO, GC_TIME_RATIO, GC_ADAPTIVE_SIZE_POLICY_WEIGHT)
These four decide how large the JVM keeps the heap. The defaults favor lower memory use at the cost of more frequent collections, and were chosen assuming the Parallel collector.
In the range where the collector is left to the JVM (memory limit of 1792 MiB or more, 2 or more CPU cores, JDK 11 and later), these four are not set. Setting sizing values tuned for a different collector while the JVM picks the collector would leave the two settings at odds. In that range the JVM defaults apply (40, 70, 12, and 10 respectively).
| Condition | Values applied |
|---|---|
| The range where the collector is left to the JVM | JVM defaults (40, 70, 12, 10) |
| Otherwise (Parallel collector) | The table defaults (10, 20, 4, 90) |
| When the environment variable is set | The value you set, regardless of range |
Memory use can rise. The JVM defaults keep a larger heap, so pods in the range where the collector is left to the JVM use more memory than before. The increase stays within the memory limit (
resources.limits.memory), so pods are not terminated, but check the node's free memory if you pack many pods onto one node.To return to the earlier behavior, set the four environment variables to the table defaults. Values set through environment variables apply regardless of range.
Garbage collection logs
When analyzing a performance problem, set ENABLE_GC_LOG to true. The logs go to standard output
rather than a file, so the cluster's log collector picks them up as they are.
env:
- name: ENABLE_GC_LOG
value: "true"
Log volume grows with the number of applications. Turn it on only while you need it, and check the log collector's storage capacity.
It is not applied on JDK 8 images. The format of the logging options changed in JDK 9, and passing that format to JDK 8 stops the container from starting.
Heap dumps
To analyze the cause of an out-of-memory error, set ENABLE_HEAP_DUMP to true. The default is
false.
Mount a volume when you enable heap dumps. Without one the dump is written inside the container and disappears when the container restarts. The file is not kept, yet it still consumes node disk.
spec:
securityContext:
fsGroup: 0
containers:
- name: myapp
env:
- name: ENABLE_HEAP_DUMP
value: "true"
volumeMounts:
- name: heapdump
mountPath: /deployments/data
volumes:
- name: heapdump
emptyDir:
sizeLimit: 2Gi
Each item is there for a reason.
fsGroup— without it the container user cannot write to the volume and the dump fails.sizeLimit— dump files accumulate on every restart. With no ceiling, an application that keeps restarting fills the node's disk and affects the other pods on that node.emptyDir— it lives as long as the pod, so it survives container restarts. It is lost if the pod moves to another node; use apersistentVolumeClaimto keep dumps longer.
Writing a dump takes time proportional to the heap, roughly 8 to 20 seconds. The container is not
terminated during that time, so a livenessProbe with a shorter timeout can cut the dump short.
Out-of-memory errors caused by running out of threads or file descriptors rather than heap produce no dump.
Application runtime:
| Variable | Description |
|---|---|
JAVA_OPTS | JVM options. Do not set this — see below |
JAVA_OPTS_APPEND | Extra JVM arguments, added to the defaults |
JAVA_APP_DIR | The location of the application directory |
JAVA_MAIN_CLASS | The Java entry point class (for example com.example.MainClass) |
JAVA_APP_NAME | A custom process name |
JAVA_ARGS | Arguments passed to the Java application |
JAVA_CLASSPATH | A custom classpath |
Do not set
JAVA_OPTS. Setting it removes every option the image builds for you — the heap size limit (JAVA_MAX_MEM_RATIO), the garbage collector choice, and the exit-on-out-of-memory setting all disappear. The JVM then ignores the pod's memory limit and sizes the heap from the node's total memory, which can end in the pod beingOOMKilled.An empty string (
"") behaves the same way, because even an empty value counts as set.To add options, use
JAVA_OPTS_APPEND(CATALINA_OPTS_APPENDon Tomcat images). They are appended after the existing options, and where an option repeats, the later value wins.
Maven build settings:
| Variable | Default | Description |
|---|---|---|
MAVEN_ARGS | - | Maven build arguments |
MAVEN_LOCAL_REPO | - | Path of the local Maven repository |
MAVEN_MIRROR_URL | - | URL of a Maven mirror repository |
MAVEN_MIRRORS | - | Support for several mirror repositories |
MAVEN_S2I_ARTIFACT_DIRS | target | The build output directory |
MAVEN_S2I_GOALS | package | The Maven goals to run |
S2I_SOURCE_DEPLOYMENTS_DIR | - | The source deployment directory |
S2I_ENABLE_INCREMENTAL_BUILDS | true | Enables incremental builds (keeps artifacts) |
Gradle build settings:
Note: The COP S2I images support Gradle builds. A Gradle build runs automatically when a
build.gradlefile is present.
| Variable | Default | Description |
|---|---|---|
GRADLE_USER_HOME | /tmp/artifacts/gradle | The Gradle user home directory (where the cache lives) |
BUILDER_ARGS | build -x test --no-daemon | Gradle build arguments |
BUILDER_ARGS_APPEND | - | Extra Gradle build arguments |
SCRIPT_DEBUG | false | true turns on script debug mode |
How the Gradle build behaves:
- Build output is copied automatically from
build/libs/to/deployments/ - JAR files with the
-plainsuffix are excluded - The Gradle Wrapper (
gradlew) is used when present; otherwise the bundled Gradle is used
Debugging and monitoring:
| Variable | Default | Description |
|---|---|---|
JAVA_DEBUG | false | Enables remote debugging |
JAVA_DEBUG_PORT | 5005 | The debug connection port |
JAVA_DIAGNOSTICS | false | Enables diagnostic output |
Proxy settings:
| Variable | Description |
|---|---|
HTTP_PROXY / http_proxy | HTTP proxy server URL |
HTTPS_PROXY / https_proxy | HTTPS proxy server URL |
NO_PROXY / no_proxy | Hosts and domains that bypass the proxy, comma-separated. The runtime JVM proxy module reads the lowercase no_proxy |
Tomcat Connector and Concurrency
These variables set the concurrency limits of the Tomcat connector (the 8080 and 8443 ports that
accept requests). They all go in the runtime (place 3) and apply only to tomcat9-* images — the
OpenJDK family does not have them. One environment variable applies the same value to both the
HTTP (8080) and HTTPS (8443) connectors.
| Variable | Default | Description |
|---|---|---|
TOMCAT_MAX_THREADS | 300 | Maximum threads handling requests at once. Raise it when concurrent requests queue up. Each thread uses 1 MB of stack outside the heap, so decide together with container memory and the database connection pool |
TOMCAT_MIN_SPARE_THREADS | 25 | Minimum spare threads kept ready |
TOMCAT_MAX_CONNECTIONS | 8192 | Maximum connections accepted at once. The NIO (non-blocking I/O) connector separates accepting connections from handling requests, so this is the value to raise for more concurrent connections |
TOMCAT_ACCEPT_COUNT | 200 | How many further connections may queue in the operating system once TOMCAT_MAX_CONNECTIONS is full |
TOMCAT_CONNECTION_TIMEOUT | 10000 | How long to wait for a request after a connection is made, in milliseconds. Set below Tomcat's default of 20000 so slow connections hold threads for less time |
TOMCAT_MAX_KEEPALIVE_REQUESTS | 200 | Maximum requests handled on one connection |
TOMCAT_MAX_POST_SIZE | 104857600 | Maximum form body size in bytes (100 MB by default). Applies only to application/x-www-form-urlencoded requests; multipart file upload size is not limited here |
TOMCAT_MAX_PARAMETER_COUNT | 10000 | Maximum parameters one request may carry. Blocks requests that consume server resources by sending parameters in bulk |
Raising
TOMCAT_MAX_THREADSto 400–500 is not recommended. More threads mean more stack memory, which runs into the container memory limit, and if each thread holds a database connection, pods × threads connections are opened and the database's connection limit is exhausted first. If you must raise it, first determine whether the bottleneck is threads, heap, or database waits; raise CPU, memory, and the database connection limit together; then adjust one step at a time while measuring.
An example, as Deployment runtime environment variables:
env:
- name: TOMCAT_MAX_THREADS
value: "300"
- name: TOMCAT_MAX_CONNECTIONS
value: "8192"
- name: TOMCAT_MAX_POST_SIZE
value: "104857600"
Tomcat HTTPS, Shutdown Port, Logging, Debug
These apply only to tomcat9-* images and all go in the runtime (place 3).
The HTTPS connector — providing all three of the variables below (_DIR, _CERTIFICATE,
_CERTIFICATE_KEY) configures an HTTPS connector on port 8443. If any one is missing, HTTPS is not
configured.
| Variable | Default | Description |
|---|---|---|
JWS_HTTPS_CERTIFICATE_DIR | None | Path of the directory holding the certificate files |
JWS_HTTPS_CERTIFICATE | None | Server certificate file name, relative to that directory |
JWS_HTTPS_CERTIFICATE_KEY | None | Server private key file name |
JWS_HTTPS_CERTIFICATE_PASSWORD | None | The private key's password, if it has one |
JWS_HTTPS_CACERTIFICATE | None | Certificate chain (intermediate CA) file name, if used |
Put certificate values in a secret. Do not leave the private key password (
JWS_HTTPS_CERTIFICATE_PASSWORD) in plain text; reference a secret instead.
env:
- name: JWS_HTTPS_CERTIFICATE_DIR
value: "/etc/tls"
- name: JWS_HTTPS_CERTIFICATE
value: "tls.crt"
- name: JWS_HTTPS_CERTIFICATE_KEY
value: "tls.key"
- name: JWS_HTTPS_CERTIFICATE_PASSWORD
valueFrom:
secretKeyRef:
name: tls-key-password
key: password # value: *****
Shutdown port, logging, debug
| Variable | Default | Description |
|---|---|---|
TOMCAT_SHUTDOWN_PORT | -1 | The Tomcat shutdown command port. The default -1 opens no port. These images handle shutdown through the SIGTERM signal, so the port is not needed. Set a port number only if you must bring it back |
TOMCAT_SHUTDOWN | Random per start | The shutdown command string. Left unset, it is randomized every time the container starts. It matters only when the shutdown port is enabled |
ENABLE_ACCESS_LOG | false | true writes the access log (AccessLogValve) to standard output |
DISABLE_REMOTE_IP_VALVE | Unset (in use) | Turns off the RemoteIpValve that restores the original client address behind a proxy |
DEBUG | None | true prints debug logs from the startup script and exposes details on Tomcat error pages (stack traces and the server version). Do not enable it in production |
JWS_SERVER_NAME | None | The connector's server attribute. Replaces the server name exposed in response headers with the value you give |
Tomcat Database Integration (JNDI, Realm)
For tomcat9-* images only, in the runtime (place 3). Use these when the application reaches a
database through JNDI (Java Naming and Directory Interface), or when Tomcat authenticates users
against a database (Realm).
JNDI data sources — there are two ways.
List prefixes in RESOURCES, separated by commas, and set the values below for each prefix.
Variable (prefix <P>) | Description |
|---|---|
RESOURCES | The list of data source prefixes (for example DB1,DB2) |
<P>_NAME | The JNDI name (for example jdbc/mydb) |
<P>_DRIVER | The JDBC driver class |
<P>_URL | The connection URL |
<P>_USERNAME | The user name |
<P>_PASSWORD | The password — put it in a secret |
<P>_TYPE | The resource type (javax.sql.DataSource by default) |
<P>_MIN_POOL_SIZE / <P>_MAX_POOL_SIZE | Minimum and maximum connection pool size (optional) |
env:
- name: RESOURCES
value: "DB1"
- name: DB1_NAME
value: "jdbc/mydb"
- name: DB1_DRIVER
value: "org.mariadb.jdbc.Driver"
- name: DB1_URL
value: "jdbc:mariadb://mariadb:3306/appdb"
- name: DB1_USERNAME
value: "appuser"
- name: DB1_PASSWORD
valueFrom:
secretKeyRef:
name: db1-secret
key: password # value: *****
DB_SERVICE_PREFIX_MAPPING configures data sources automatically from Kubernetes service
environment variables. The format is <service name>=<prefix>, and it reads that service's
_SERVICE_HOST and _SERVICE_PORT.
Database-backed authentication (DataSourceRealm) — providing all of the variables below makes Tomcat authenticate users through the named data source. If any one is missing, the Realm is not configured.
| Variable | Default | Description |
|---|---|---|
JWS_REALM_DATASOURCE_NAME | jdbc/auth | The JNDI data source used for authentication |
JWS_REALM_USERTABLE | None | The user table name |
JWS_REALM_USERNAME_COL | None | The user name column |
JWS_REALM_USERCRED_COL | None | The password (credential) column |
JWS_REALM_USERROLE_TABLE | None | The user-to-role mapping table |
JWS_REALM_ROLENAME_COL | None | The role name column |
The table and column names may be exposed as they are, but the database password the data source uses goes in a secret, as in the
RESOURCESexample above.
Node.js
Note: Details of the Node.js S2I container image environment variables:
Runtime settings:
| Variable | Default | Description |
|---|---|---|
NODE_ENV | production | The Node.js runtime mode (production / development) |
NPM_RUN | start | The npm script to run, from the scripts section of package.json |
NODE_CMD | - | A custom start command used instead of npm start |
INIT_WRAPPER | false | true starts the application through the init-wrapper script |
Build settings:
| Variable | Default | Description |
|---|---|---|
NPM_BUILD | build | The npm script to run at build time, from the scripts section of package.json |
NPM_MIRROR | - | A custom registry mirror URL for downloading npm packages |
npm_config_loglevel | - | The log level during npm install |
Development mode:
| Variable | Default | Description |
|---|---|---|
DEV_MODE | false | true enables automatic server restarts with nodemon |
DEBUG_PORT | 5858 | The debug port, effective only when DEV_MODE=true |
Note: With
DEV_MODE=true,NODE_ENVchanges todevelopmentautomatically unless it is set explicitly.
Proxy settings:
| Variable | Description |
|---|---|
HTTP_PROXY | The npm proxy URL at build time |
HTTPS_PROXY | The npm proxy URL at build time (HTTPS) |
Python
Note: Details of the Python S2I container image environment variables:
Application startup settings:
| Variable | Default | Description |
|---|---|---|
APP_SCRIPT | app.sh | Path of the application start script |
APP_FILE | app.py | Path of the Python file passed to the interpreter |
APP_MODULE | application | The WSGI callable pattern (MODULE_NAME:VARIABLE_NAME) |
APP_HOME | . (root) | The subdirectory holding wsgi.py or manage.py |
APP_CONFIG | - | Path of the Gunicorn configuration file |
PORT | 8080 | The HTTP listening port |
Gunicorn settings:
| Variable | Default | Description |
|---|---|---|
WEB_CONCURRENCY | (cores × 2), up to 12 | The number of Gunicorn workers |
Django settings:
| Variable | Default | Description |
|---|---|---|
DISABLE_MIGRATE | false | true skips manage.py migrate |
DISABLE_COLLECTSTATIC | false | true skips manage.py collectstatic |
Dependency management:
| Variable | Default | Description |
|---|---|---|
ENABLE_PIPENV | false | true manages dependencies with Pipenv |
PIN_PIPENV_VERSION | - | Pins a specific Pipenv version (for example 2018.11.26) |
ENABLE_MICROPIPENV | false | true uses the micropipenv wrapper (supports requirements.txt, Pipenv, and Poetry) |
PIP_INDEX_URL | PyPI | A custom PyPI index or mirror URL |
UPGRADE_PIP_TO_LATEST | false | true upgrades pip, setuptools, and wheel to their latest versions |
Build options:
| Variable | Default | Description |
|---|---|---|
DISABLE_SETUP_PY_PROCESSING | false | true skips setup.py processing |
ENABLE_INIT_WRAPPER | false | true enables the init wrapper that reaps zombie processes |
PHP
Note: Details of the PHP S2I container image environment variables:
Basic PHP settings:
| Variable | Default | Description |
|---|---|---|
PHP_MEMORY_LIMIT | 128M | The PHP memory limit |
DOCUMENTROOT | / | The DocumentRoot path (for example /public) |
INCLUDE_PATH | .:/opt/app-root/src:/usr/share/pear | PHP source file paths |
SHORT_OPEN_TAG | OFF | Whether <? ?> short tags are recognized |
PHP_CLEAR_ENV | ON | Clears environment variables in FPM workers |
Error handling:
| Variable | Default | Description |
|---|---|---|
ERROR_REPORTING | E_ALL & ~E_NOTICE | The PHP error reporting level |
DISPLAY_ERRORS | ON | Whether errors, warnings, and notices are printed |
DISPLAY_STARTUP_ERRORS | OFF | Shows errors during PHP startup |
HTML_ERRORS | ON | Links errors to documentation |
Session settings:
| Variable | Default | Description |
|---|---|---|
SESSION_NAME | PHPSESSID | The session name |
SESSION_HANDLER | files | How sessions are stored |
SESSION_PATH | /tmp/sessions | Where session data files live |
SESSION_COOKIE_DOMAIN | - | The domain the cookie is valid for |
SESSION_COOKIE_HTTPONLY | 0 | Whether the httpOnly flag is added |
SESSION_COOKIE_SECURE | Off | Whether the cookie is HTTPS-only |
OPcache settings:
| Variable | Default | Description |
|---|---|---|
OPCACHE_MEMORY_CONSUMPTION | 128 | OPcache shared memory size (MB) |
OPCACHE_REVALIDATE_FREQ | 2 | How often script timestamps are checked (seconds) |
OPCACHE_MAX_FILES | 4000 | Maximum keys (scripts) in the OPcache hash table |
Apache MPM settings:
| Variable | Default | Description |
|---|---|---|
HTTPD_START_SERVERS | 8 | Child server processes created at startup |
HTTPD_MAX_REQUEST_WORKERS | 256 | Maximum requests handled at once |
HTTPD_MAX_REQUESTS_PER_CHILD | 4000 | Maximum connections handled by one child process |
HTTPD_MAX_KEEPALIVE_REQUESTS | 100 | Maximum requests allowed per connection |
Composer settings:
| Variable | Default | Description |
|---|---|---|
COMPOSER_MIRROR | - | A custom Composer repository mirror URL |
COMPOSER_INSTALLER | - | Overrides the Composer download URL |
COMPOSER_VERSION | - | The Composer version to install |
COMPOSER_ARGS | - | Extra arguments for composer install |
Overriding PHP configuration:
| Variable | Description |
|---|---|
PHPRC | Path of the php.ini file |
PHP_INI_SCAN_DIR | Path scanned for additional ini files |
Ruby
Note: Details of the Ruby S2I container image environment variables:
Application environment:
| Variable | Default | Description |
|---|---|---|
RACK_ENV | - | The deployment environment (production, development, test) |
RAILS_ENV | - | The Rails application environment (set to development for hot deploy) |
DISABLE_ASSET_COMPILATION | - | true skips asset compilation (production only) |
Note: Asset compilation runs only in the
productionenvironment. For hot deploy in development, setRAILS_ENV=developmentorRACK_ENV=development.
Puma web server settings:
| Variable | Default | Description |
|---|---|---|
PUMA_MIN_THREADS | - | Minimum threads in the Puma thread pool |
PUMA_MAX_THREADS | - | Maximum threads in the Puma thread pool |
PUMA_WORKERS | CPU cores | Worker processes to run in cluster mode |
Package management:
| Variable | Default | Description |
|---|---|---|
RUBYGEM_MIRROR | - | A RubyGems mirror URL, for downloading gems at build time |
Nginx
Note: Details of the Nginx container image environment variables:
| Variable | Default | Description |
|---|---|---|
NGINX_LOG_TO_VOLUME | - | When set, writes logs to /var/log/nginx/ (default: stdout/stderr) |
Note: Nginx is configured mainly through configuration files rather than environment variables:
./nginx.conf— the main configuration file./nginx-cfg/*.conf— additional nginx configuration./nginx-default-cfg/*.conf— default server block snippets./nginx-start/*.sh— shell scripts run before nginx starts
httpd
Note: Details of the Apache httpd container image environment variables:
| Variable | Default | Description |
|---|---|---|
HTTPD_LOG_TO_VOLUME | - | When set, writes logs to /var/log/httpd24 (default: stdout) |
HTTPD_MPM | prefork | The Multi-Processing Module (event, prefork, worker) |
Note: httpd is configured mainly through configuration files:
./httpd-cfg/*.conf— additional httpd configuration./httpd-pre-init/*.sh— shell scripts run before httpd starts./httpd-ssl/certs/— the SSL certificate directory./httpd-ssl/private/— the SSL private key directory
Varnish
Note: Details of the Varnish container image:
The Varnish container is configured mainly through VCL (Varnish Configuration Language) files, with the two environment variables below. Both go in the runtime (place 3).
| Variable | Default | Description |
|---|---|---|
VARNISH_VCL | /etc/varnish/default.vcl | Path of the VCL file Varnish reads. Change it to use a VCL elsewhere |
VARNISH_TTL | 120 | Default cache lifetime in seconds, applied to responses the VCL does not set explicitly |
How to configure the VCL:
Include a default.vcl file in the source directory at S2I build time to define the Varnish
configuration:
# default.vcl example
vcl 4.0;
backend default {
.host = "backend-service";
.port = "8080";
}
sub vcl_recv {
# request handling logic
}
sub vcl_backend_response {
# backend response handling logic
}
Note: Varnish logs go to stdout by default and can be read with
podman logsorkubectl logs.
MariaDB
Note: Details of the MariaDB container image environment variables:
Put passwords in a secret. The password variables of the database images (MariaDB, MySQL, PostgreSQL, Redis),
MYSQL_PASSWORDandMYSQL_ROOT_PASSWORDamong them, should not sit in plain text in the Deploymentenv; reference a secret withvalueFrom.secretKeyRefinstead (see the Tomcat database integration section for an example).
Required environment variables:
| Variable | Description |
|---|---|
MYSQL_USER | The database user to create |
MYSQL_PASSWORD | That user's password |
MYSQL_DATABASE | The database to create |
Optional environment variables:
| Variable | Default | Description |
|---|---|---|
MYSQL_ROOT_PASSWORD | - | The root password, needed when remote access is allowed |
MYSQL_CHARSET | utf8 | The default character set |
MYSQL_COLLATION | utf8_general_ci | The default collation |
Performance tuning:
| Variable | Default | Description |
|---|---|---|
MYSQL_MAX_CONNECTIONS | 151 | Maximum concurrent client connections |
MYSQL_MAX_ALLOWED_PACKET | 200M | Maximum size of a packet or generated string |
MYSQL_TABLE_OPEN_CACHE | 400 | Open table cache entries across all threads |
MYSQL_KEY_BUFFER_SIZE | 32M or 10% of memory | Index block buffer size |
MYSQL_SORT_BUFFER_SIZE | 256K | Sort operation buffer size |
MYSQL_READ_BUFFER_SIZE | 8M or 5% of memory | Sequential scan buffer size |
MYSQL_INNODB_BUFFER_POOL_SIZE | 32M or 50% of memory | InnoDB table and index cache buffer |
MYSQL_INNODB_LOG_FILE_SIZE | 8M or 15% of memory | Size of each log file |
MYSQL_INNODB_LOG_BUFFER_SIZE | 8M or 15% of memory | InnoDB disk log write buffer |
Full-text search settings:
| Variable | Default | Description |
|---|---|---|
MYSQL_FT_MIN_WORD_LEN | 4 | Minimum word length for a FULLTEXT index |
MYSQL_FT_MAX_WORD_LEN | 20 | Maximum word length for a FULLTEXT index |
Advanced settings:
| Variable | Default | Description |
|---|---|---|
MYSQL_LOWER_CASE_TABLE_NAMES | 0 | Controls case sensitivity of table names |
MYSQL_AIO | 1 | Sets innodb_use_native_aio |
MYSQL_BINLOG_FORMAT | statement | The replication log format (row, statement) |
MYSQL_LOG_QUERIES_ENABLED | 0 | 1 enables query logging |
MYSQL_DEFAULTS_FILE | /etc/my.cnf | Path of an alternative configuration file |
MYSQL_DATADIR_ACTION | upgrade-warn | Upgrade behavior (upgrade-auto, upgrade-force, optimize, analyze, disable) |
Data directory:
The container's data directory is /var/lib/mysql/data. Mount a volume for persistent storage.
MySQL
Note: Details of the MySQL container image environment variables:
Required environment variables:
| Variable | Description |
|---|---|
MYSQL_USER | The database user to create |
MYSQL_PASSWORD | That user's password |
MYSQL_DATABASE | The database to create |
Optional environment variables:
| Variable | Default | Description |
|---|---|---|
MYSQL_ROOT_PASSWORD | - | The root password, needed when remote access is allowed |
MYSQL_CHARSET | utf8mb4 | The default character set |
MYSQL_COLLATION | utf8mb4_0900_ai_ci | The default collation |
Authentication settings (MySQL 8.x only):
| Variable | Default | Description |
|---|---|---|
MYSQL_AUTHENTICATION_POLICY | caching_sha2_password,, | The authentication policy |
MYSQL_DEFAULT_AUTHENTICATION_PLUGIN | caching_sha2_password | The default authentication plugin (mysql_native_password, caching_sha2_password) — deprecated |
Note: MySQL 8.x uses
caching_sha2_passwordas its default authentication plugin. Change it tomysql_native_passwordif you need compatibility with legacy clients.
Performance tuning:
| Variable | Default | Description |
|---|---|---|
MYSQL_MAX_CONNECTIONS | 151 | Maximum concurrent client connections |
MYSQL_MAX_ALLOWED_PACKET | 200M | Maximum size of a packet or generated string |
MYSQL_TABLE_OPEN_CACHE | 400 | Open table cache entries across all threads |
MYSQL_KEY_BUFFER_SIZE | 32M or 10% of memory | Index block buffer size |
MYSQL_SORT_BUFFER_SIZE | 256K | Sort operation buffer size |
MYSQL_READ_BUFFER_SIZE | 8M or 5% of memory | Sequential scan buffer size |
MYSQL_INNODB_BUFFER_POOL_SIZE | 32M or 50% of memory | InnoDB table and index cache buffer |
MYSQL_INNODB_LOG_FILE_SIZE | 8M or 15% of memory | Size of each log file |
MYSQL_INNODB_LOG_BUFFER_SIZE | 8M or 15% of memory | InnoDB disk log write buffer |
Full-text search settings:
| Variable | Default | Description |
|---|---|---|
MYSQL_FT_MIN_WORD_LEN | 4 | Minimum word length for a FULLTEXT index |
MYSQL_FT_MAX_WORD_LEN | 20 | Maximum word length for a FULLTEXT index |
Advanced settings:
| Variable | Default | Description |
|---|---|---|
MYSQL_LOWER_CASE_TABLE_NAMES | 0 | Controls case sensitivity of table names |
MYSQL_AIO | 1 | Sets innodb_use_native_aio |
MYSQL_BINLOG_FORMAT | statement | The replication log format (row, statement) |
MYSQL_LOG_QUERIES_ENABLED | 0 | 1 enables query logging |
MYSQL_DEFAULTS_FILE | /etc/my.cnf | Path of an alternative configuration file |
MYSQL_DATADIR_ACTION | - | Data directory action (optimize, analyze — comma-separated) |
Data directory:
The container's data directory is /var/lib/mysql/data. Mount a volume for persistent storage.
PostgreSQL
Note: Details of the PostgreSQL container image environment variables:
Required environment variables:
| Variable | Description |
|---|---|
POSTGRESQL_USER | The database user to create |
POSTGRESQL_PASSWORD | That user's password |
POSTGRESQL_DATABASE | The database to create |
Optional environment variables:
| Variable | Default | Description |
|---|---|---|
POSTGRESQL_ADMIN_PASSWORD | - | The password of the postgres administrator account |
Performance tuning:
| Variable | Default | Description |
|---|---|---|
POSTGRESQL_MAX_CONNECTIONS | 100 | Maximum client connections |
POSTGRESQL_MAX_PREPARED_TRANSACTIONS | 0 | Maximum transactions in the prepared state |
POSTGRESQL_SHARED_BUFFERS | 1/4 of memory or 32M | Shared memory for caching data |
POSTGRESQL_EFFECTIVE_CACHE_SIZE | 1/2 of memory or 128M | Memory assumed available for the disk cache |
Logging and extensions:
| Variable | Default | Description |
|---|---|---|
POSTGRESQL_LOG_DESTINATION | /var/lib/pgsql/data/userdata/log/ | Where error logs are written |
POSTGRESQL_LIBRARIES | - | Libraries to load in shared_preload_libraries (comma-separated) |
POSTGRESQL_EXTENSIONS | - | Extensions to create at server start (space-separated) |
Data migration:
| Variable | Default | Description |
|---|---|---|
POSTGRESQL_MIGRATION_REMOTE_HOST | - | Host name or IP of the migration source |
POSTGRESQL_MIGRATION_ADMIN_PASSWORD | - | The remote postgres administrator password |
POSTGRESQL_MIGRATION_IGNORE_ERRORS | no | yes ignores errors while importing SQL |
Database upgrade:
| Variable | Description |
|---|---|
POSTGRESQL_UPGRADE | Upgrade from PostgreSQL 15 (copy or hardlink) |
Data directory:
The container's data directory is /var/lib/pgsql/data. Mount a volume for persistent storage.
Redis
Note: Details of the Redis container image environment variables:
Environment variables:
| Variable | Default | Description |
|---|---|---|
REDIS_PASSWORD | - | The password for access to the Redis server (optional) |
Security note: Redis can process roughly 150,000 passwords per second, so use a long password that cannot be guessed.
Data directory:
The container's data directory is /var/lib/redis/data. Mount a volume for persistent storage.
Customizing the Redis configuration: When further Redis configuration is needed, mount a configuration file.
Security Settings in the Tomcat S2I Images
The tomcat9-*-s2i images ship with the security settings below already applied. All five images
(tomcat9-jdk8-ubi8-s2i, tomcat9-jdk11-ubi8-s2i, tomcat9-jdk17-ubi8-s2i,
tomcat9-jdk17-ubi9-s2i, tomcat9-jdk21-ubi9-s2i) are identical in this respect.
They are collected here so they can be cited as evidence in a vulnerability assessment. The environment variables that change each item are in the OpenJDK/Tomcat common section and the Tomcat HTTPS, shutdown port, logging, and debug section above.
| Item | What is applied | If reversed |
|---|---|---|
| Non-root execution | The container runs as UID 185 | Do not change this with another setting |
| Management and example web apps removed | manager, host-manager, docs, examples, and ROOT are deleted from the image | — |
| Shutdown port | Not opened (TOMCAT_SHUTDOWN_PORT defaults to -1) | Setting a port makes it accept shutdown commands there |
| Shutdown command string | Randomized on every start | Setting TOMCAT_SHUTDOWN fixes it to that value |
| AJP connector | Not used (port 8009 is not opened) | — |
| Error pages | Do not show stack traces or the Tomcat version | DEBUG=true shows both |
| Session cookie | Carries HttpOnly so scripts cannot read it | — |
| Web application privileges | Cannot reach the container's internal API (privileged="false") | — |
| Client address | Restores the original address behind a proxy (RemoteIpValve) | DISABLE_REMOTE_IP_VALVE turns it off |
Why the management web apps were removed
Tomcat's manager and host-manager deploy applications and administer the server remotely. In a
container environment, deployment happens through an S2I build and a pod restart, so they are not
needed; leaving them in place gives anyone past authentication a way to upload arbitrary
applications. examples includes samples that manipulate sessions and cookies, and docs contains
the Tomcat version.
The image deletes those five and links the webapps directory to /deployments, so the only web
application in a running container is the one the S2I build produced.
A warning about the DEBUG environment variable
🚨
DEBUG=truedoes not merely add logs. It also exposes stack traces and the server version on Tomcat error pages. Do not enable it in production. Turn it on only while reproducing a problem, and turn it back off afterwards.
HTTPS is not configured by default
⚠️ The image declares port 8443, but in its default state it does not listen on that port. An HTTPS connector is created only when all three certificate environment variables from the Tomcat HTTPS, shutdown port, logging, and debug section (
JWS_HTTPS_CERTIFICATE_DIR,JWS_HTTPS_CERTIFICATE,JWS_HTTPS_CERTIFICATE_KEY) are provided. If any one is missing, it is not configured.Inside a cluster the Ingress usually terminates TLS, so HTTPS does not need to reach the container. Configure it only when TLS must terminate in the container, and reference a secret rather than leaving the private key password (
JWS_HTTPS_CERTIFICATE_PASSWORD) in plain text.
UID 185 and group 0
We recommend runAsUser: 185 together with runAsGroup: 0 in the pod's securityContext. The
reason follows.
The image's files are owned by 185:0, and the group is given the same permissions as the owner.
A process assigned a different UID can therefore still read and write the application directory as
long as it belongs to group 0. This lets the image keep working where a security policy requires a
runAsUser other than 185.
runAsGroup: 0 does not mean the container process runs as the root user. The user is the non-root
user given by runAsNonRoot: true and runAsUser.
Items not applied
| Item | Status |
|---|---|
The Tomcat security listener (SecurityListener) | Not applied. The listener blocks root execution and checks umask, and running as UID 185 achieves the same purpose |
The TRACE method | Not allowed, per Tomcat's default configuration |
The X-Powered-By response header | Not emitted, per Tomcat's default configuration |