Skip to content

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.

ItemValue
Nameopenmaru-war-example
NamespaceThe same namespace as the eGovFrame sample (egov_project_name)
SourceThe openmaru-war-example repository the installation created in GitLab (main branch)
Builder imagetomcat9-jdk8-ubi8-s2i-openmaru:9.0
Access secretopenmaru-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.

PlaceAppliesWhere you put itTypical variables
1. BuildOnly while the source is turned into an imageBuildConfig spec.strategy.sourceStrategy.env, the environment variable rows of the Console's [Build settings] form, s2i build -eMAVEN_MIRROR_URL, MAVEN_ARGS_APPEND, S2I_SOURCE_DEPLOYMENTS_FILTER
2. .s2i/environmentBoth build and runtimeThe standard file kept in the source repositoryValues needed by both build and run
3. RuntimeWhen the container startsDeployment spec.template.spec.containers[].env, the environment variables on the Console's deployment screenTOMCAT_*, JWS_HTTPS_*, JWS_REALM_*, RESOURCES, CATALINA_OPTS_APPEND

The wrong place is ignored without an error. Putting the runtime variable TOMCAT_MAX_THREADS into the build (1) leaves the build successful, the value discarded, and the running container unchanged. Putting the build variable MAVEN_MIRROR_URL into 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):

VariableValueDescription
JAVA_HOME/usr/lib/jvm/java-{version}The JDK installation directory
JAVA_VERSION1.8.0, 11, 17, 21The OpenJDK version
JAVA_VENDORopenjdkThe Java distribution type
HOME/home/jboss (UBI8 family), /home/default (UBI9 family)The default user's home directory
USERjboss (UBI8 family), default (UBI9 family)The container user. The name differs by family, but the UID is 185 in both
JAVA_DATA_DIR/deployments/dataThe application data directory
MAVEN_VERSION3.8 (UBI8 family), 3.9 (UBI9 family)The bundled Maven version
GRADLE_VERSION8.14.5 (UBI8 family), 9.7.1 (UBI9 family)The bundled Gradle version

JVM memory and GC settings:

VariableDefaultDescription
JAVA_MAX_MEM_RATIO70 (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_HEAP4096mThe heap size to use when the container has no memory limit. See below
GC_CONTAINER_OPTIONSChosen automatically (see below)The garbage collector. Setting a value overrides the automatic choice
GC_MIN_HEAP_FREE_RATIO10Minimum percentage of free space to keep after a collection. See below
GC_MAX_HEAP_FREE_RATIO20Maximum percentage of free space to keep after a collection. See below
GC_TIME_RATIO4The inverse of the time that may be spent in garbage collection. See below
GC_ADAPTIVE_SIZE_POLICY_WEIGHT90Weight given to recent observations when resizing the heap (%). See below
GC_METASPACE_SIZE20Initial metaspace size (MB)
GC_MAX_METASPACE_SIZE100Maximum metaspace size (MB)
ENABLE_GC_LOGfalsetrue writes garbage collection logs to standard output. Not applied on JDK 8 images
ENABLE_HEAP_DUMPfalsetrue writes a heap dump on an out-of-memory error
HEAP_DUMP_PATH/deployments/dataThe 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_HEAP is a safety net for when the limit was forgotten; it does not replace one.

The default heap ratio differs by image family.

Image familyJAVA_MAX_MEM_RATIO defaultMaximum heap in a 2 GB pod
OpenJDK (openjdk*-s2i)70About 1.4 GB
Tomcat (tomcat9-*-s2i)50About 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_RATIO further.

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_RATIO explicitly.

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.

ConditionCollector 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
OtherwiseParallel
JDK 8 imagesParallel, 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).

ConditionValues applied
The range where the collector is left to the JVMJVM defaults (40, 70, 12, 10)
Otherwise (Parallel collector)The table defaults (10, 20, 4, 90)
When the environment variable is setThe 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 a persistentVolumeClaim to 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:

VariableDescription
JAVA_OPTSJVM options. Do not set this — see below
JAVA_OPTS_APPENDExtra JVM arguments, added to the defaults
JAVA_APP_DIRThe location of the application directory
JAVA_MAIN_CLASSThe Java entry point class (for example com.example.MainClass)
JAVA_APP_NAMEA custom process name
JAVA_ARGSArguments passed to the Java application
JAVA_CLASSPATHA 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 being OOMKilled.

An empty string ("") behaves the same way, because even an empty value counts as set.

To add options, use JAVA_OPTS_APPEND (CATALINA_OPTS_APPEND on Tomcat images). They are appended after the existing options, and where an option repeats, the later value wins.

Maven build settings:

VariableDefaultDescription
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_DIRStargetThe build output directory
MAVEN_S2I_GOALSpackageThe Maven goals to run
S2I_SOURCE_DEPLOYMENTS_DIR-The source deployment directory
S2I_ENABLE_INCREMENTAL_BUILDStrueEnables incremental builds (keeps artifacts)

Gradle build settings:

Note: The COP S2I images support Gradle builds. A Gradle build runs automatically when a build.gradle file is present.

VariableDefaultDescription
GRADLE_USER_HOME/tmp/artifacts/gradleThe Gradle user home directory (where the cache lives)
BUILDER_ARGSbuild -x test --no-daemonGradle build arguments
BUILDER_ARGS_APPEND-Extra Gradle build arguments
SCRIPT_DEBUGfalsetrue turns on script debug mode

How the Gradle build behaves:

  • Build output is copied automatically from build/libs/ to /deployments/
  • JAR files with the -plain suffix are excluded
  • The Gradle Wrapper (gradlew) is used when present; otherwise the bundled Gradle is used

Debugging and monitoring:

VariableDefaultDescription
JAVA_DEBUGfalseEnables remote debugging
JAVA_DEBUG_PORT5005The debug connection port
JAVA_DIAGNOSTICSfalseEnables diagnostic output

Proxy settings:

VariableDescription
HTTP_PROXY / http_proxyHTTP proxy server URL
HTTPS_PROXY / https_proxyHTTPS proxy server URL
NO_PROXY / no_proxyHosts 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.

VariableDefaultDescription
TOMCAT_MAX_THREADS300Maximum 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_THREADS25Minimum spare threads kept ready
TOMCAT_MAX_CONNECTIONS8192Maximum 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_COUNT200How many further connections may queue in the operating system once TOMCAT_MAX_CONNECTIONS is full
TOMCAT_CONNECTION_TIMEOUT10000How 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_REQUESTS200Maximum requests handled on one connection
TOMCAT_MAX_POST_SIZE104857600Maximum 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_COUNT10000Maximum parameters one request may carry. Blocks requests that consume server resources by sending parameters in bulk

Raising TOMCAT_MAX_THREADS to 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.

VariableDefaultDescription
JWS_HTTPS_CERTIFICATE_DIRNonePath of the directory holding the certificate files
JWS_HTTPS_CERTIFICATENoneServer certificate file name, relative to that directory
JWS_HTTPS_CERTIFICATE_KEYNoneServer private key file name
JWS_HTTPS_CERTIFICATE_PASSWORDNoneThe private key's password, if it has one
JWS_HTTPS_CACERTIFICATENoneCertificate 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

VariableDefaultDescription
TOMCAT_SHUTDOWN_PORT-1The 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_SHUTDOWNRandom per startThe shutdown command string. Left unset, it is randomized every time the container starts. It matters only when the shutdown port is enabled
ENABLE_ACCESS_LOGfalsetrue writes the access log (AccessLogValve) to standard output
DISABLE_REMOTE_IP_VALVEUnset (in use)Turns off the RemoteIpValve that restores the original client address behind a proxy
DEBUGNonetrue 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_NAMENoneThe 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
RESOURCESThe list of data source prefixes (for example DB1,DB2)
<P>_NAMEThe JNDI name (for example jdbc/mydb)
<P>_DRIVERThe JDBC driver class
<P>_URLThe connection URL
<P>_USERNAMEThe user name
<P>_PASSWORDThe password — put it in a secret
<P>_TYPEThe resource type (javax.sql.DataSource by default)
<P>_MIN_POOL_SIZE / <P>_MAX_POOL_SIZEMinimum 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.

VariableDefaultDescription
JWS_REALM_DATASOURCE_NAMEjdbc/authThe JNDI data source used for authentication
JWS_REALM_USERTABLENoneThe user table name
JWS_REALM_USERNAME_COLNoneThe user name column
JWS_REALM_USERCRED_COLNoneThe password (credential) column
JWS_REALM_USERROLE_TABLENoneThe user-to-role mapping table
JWS_REALM_ROLENAME_COLNoneThe 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 RESOURCES example above.

Node.js

Note: Details of the Node.js S2I container image environment variables:

Runtime settings:

VariableDefaultDescription
NODE_ENVproductionThe Node.js runtime mode (production / development)
NPM_RUNstartThe npm script to run, from the scripts section of package.json
NODE_CMD-A custom start command used instead of npm start
INIT_WRAPPERfalsetrue starts the application through the init-wrapper script

Build settings:

VariableDefaultDescription
NPM_BUILDbuildThe 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:

VariableDefaultDescription
DEV_MODEfalsetrue enables automatic server restarts with nodemon
DEBUG_PORT5858The debug port, effective only when DEV_MODE=true

Note: With DEV_MODE=true, NODE_ENV changes to development automatically unless it is set explicitly.

Proxy settings:

VariableDescription
HTTP_PROXYThe npm proxy URL at build time
HTTPS_PROXYThe npm proxy URL at build time (HTTPS)

Python

Note: Details of the Python S2I container image environment variables:

Application startup settings:

VariableDefaultDescription
APP_SCRIPTapp.shPath of the application start script
APP_FILEapp.pyPath of the Python file passed to the interpreter
APP_MODULEapplicationThe 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
PORT8080The HTTP listening port

Gunicorn settings:

VariableDefaultDescription
WEB_CONCURRENCY(cores × 2), up to 12The number of Gunicorn workers

Django settings:

VariableDefaultDescription
DISABLE_MIGRATEfalsetrue skips manage.py migrate
DISABLE_COLLECTSTATICfalsetrue skips manage.py collectstatic

Dependency management:

VariableDefaultDescription
ENABLE_PIPENVfalsetrue manages dependencies with Pipenv
PIN_PIPENV_VERSION-Pins a specific Pipenv version (for example 2018.11.26)
ENABLE_MICROPIPENVfalsetrue uses the micropipenv wrapper (supports requirements.txt, Pipenv, and Poetry)
PIP_INDEX_URLPyPIA custom PyPI index or mirror URL
UPGRADE_PIP_TO_LATESTfalsetrue upgrades pip, setuptools, and wheel to their latest versions

Build options:

VariableDefaultDescription
DISABLE_SETUP_PY_PROCESSINGfalsetrue skips setup.py processing
ENABLE_INIT_WRAPPERfalsetrue enables the init wrapper that reaps zombie processes

PHP

Note: Details of the PHP S2I container image environment variables:

Basic PHP settings:

VariableDefaultDescription
PHP_MEMORY_LIMIT128MThe PHP memory limit
DOCUMENTROOT/The DocumentRoot path (for example /public)
INCLUDE_PATH.:/opt/app-root/src:/usr/share/pearPHP source file paths
SHORT_OPEN_TAGOFFWhether <? ?> short tags are recognized
PHP_CLEAR_ENVONClears environment variables in FPM workers

Error handling:

VariableDefaultDescription
ERROR_REPORTINGE_ALL & ~E_NOTICEThe PHP error reporting level
DISPLAY_ERRORSONWhether errors, warnings, and notices are printed
DISPLAY_STARTUP_ERRORSOFFShows errors during PHP startup
HTML_ERRORSONLinks errors to documentation

Session settings:

VariableDefaultDescription
SESSION_NAMEPHPSESSIDThe session name
SESSION_HANDLERfilesHow sessions are stored
SESSION_PATH/tmp/sessionsWhere session data files live
SESSION_COOKIE_DOMAIN-The domain the cookie is valid for
SESSION_COOKIE_HTTPONLY0Whether the httpOnly flag is added
SESSION_COOKIE_SECUREOffWhether the cookie is HTTPS-only

OPcache settings:

VariableDefaultDescription
OPCACHE_MEMORY_CONSUMPTION128OPcache shared memory size (MB)
OPCACHE_REVALIDATE_FREQ2How often script timestamps are checked (seconds)
OPCACHE_MAX_FILES4000Maximum keys (scripts) in the OPcache hash table

Apache MPM settings:

VariableDefaultDescription
HTTPD_START_SERVERS8Child server processes created at startup
HTTPD_MAX_REQUEST_WORKERS256Maximum requests handled at once
HTTPD_MAX_REQUESTS_PER_CHILD4000Maximum connections handled by one child process
HTTPD_MAX_KEEPALIVE_REQUESTS100Maximum requests allowed per connection

Composer settings:

VariableDefaultDescription
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:

VariableDescription
PHPRCPath of the php.ini file
PHP_INI_SCAN_DIRPath scanned for additional ini files

Ruby

Note: Details of the Ruby S2I container image environment variables:

Application environment:

VariableDefaultDescription
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 production environment. For hot deploy in development, set RAILS_ENV=development or RACK_ENV=development.

Puma web server settings:

VariableDefaultDescription
PUMA_MIN_THREADS-Minimum threads in the Puma thread pool
PUMA_MAX_THREADS-Maximum threads in the Puma thread pool
PUMA_WORKERSCPU coresWorker processes to run in cluster mode

Package management:

VariableDefaultDescription
RUBYGEM_MIRROR-A RubyGems mirror URL, for downloading gems at build time

Nginx

Note: Details of the Nginx container image environment variables:

VariableDefaultDescription
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:

VariableDefaultDescription
HTTPD_LOG_TO_VOLUME-When set, writes logs to /var/log/httpd24 (default: stdout)
HTTPD_MPMpreforkThe 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).

VariableDefaultDescription
VARNISH_VCL/etc/varnish/default.vclPath of the VCL file Varnish reads. Change it to use a VCL elsewhere
VARNISH_TTL120Default 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 logs or kubectl 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_PASSWORD and MYSQL_ROOT_PASSWORD among them, should not sit in plain text in the Deployment env; reference a secret with valueFrom.secretKeyRef instead (see the Tomcat database integration section for an example).

Required environment variables:

VariableDescription
MYSQL_USERThe database user to create
MYSQL_PASSWORDThat user's password
MYSQL_DATABASEThe database to create

Optional environment variables:

VariableDefaultDescription
MYSQL_ROOT_PASSWORD-The root password, needed when remote access is allowed
MYSQL_CHARSETutf8The default character set
MYSQL_COLLATIONutf8_general_ciThe default collation

Performance tuning:

VariableDefaultDescription
MYSQL_MAX_CONNECTIONS151Maximum concurrent client connections
MYSQL_MAX_ALLOWED_PACKET200MMaximum size of a packet or generated string
MYSQL_TABLE_OPEN_CACHE400Open table cache entries across all threads
MYSQL_KEY_BUFFER_SIZE32M or 10% of memoryIndex block buffer size
MYSQL_SORT_BUFFER_SIZE256KSort operation buffer size
MYSQL_READ_BUFFER_SIZE8M or 5% of memorySequential scan buffer size
MYSQL_INNODB_BUFFER_POOL_SIZE32M or 50% of memoryInnoDB table and index cache buffer
MYSQL_INNODB_LOG_FILE_SIZE8M or 15% of memorySize of each log file
MYSQL_INNODB_LOG_BUFFER_SIZE8M or 15% of memoryInnoDB disk log write buffer

Full-text search settings:

VariableDefaultDescription
MYSQL_FT_MIN_WORD_LEN4Minimum word length for a FULLTEXT index
MYSQL_FT_MAX_WORD_LEN20Maximum word length for a FULLTEXT index

Advanced settings:

VariableDefaultDescription
MYSQL_LOWER_CASE_TABLE_NAMES0Controls case sensitivity of table names
MYSQL_AIO1Sets innodb_use_native_aio
MYSQL_BINLOG_FORMATstatementThe replication log format (row, statement)
MYSQL_LOG_QUERIES_ENABLED01 enables query logging
MYSQL_DEFAULTS_FILE/etc/my.cnfPath of an alternative configuration file
MYSQL_DATADIR_ACTIONupgrade-warnUpgrade 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:

VariableDescription
MYSQL_USERThe database user to create
MYSQL_PASSWORDThat user's password
MYSQL_DATABASEThe database to create

Optional environment variables:

VariableDefaultDescription
MYSQL_ROOT_PASSWORD-The root password, needed when remote access is allowed
MYSQL_CHARSETutf8mb4The default character set
MYSQL_COLLATIONutf8mb4_0900_ai_ciThe default collation

Authentication settings (MySQL 8.x only):

VariableDefaultDescription
MYSQL_AUTHENTICATION_POLICYcaching_sha2_password,,The authentication policy
MYSQL_DEFAULT_AUTHENTICATION_PLUGINcaching_sha2_passwordThe default authentication plugin (mysql_native_password, caching_sha2_password) — deprecated

Note: MySQL 8.x uses caching_sha2_password as its default authentication plugin. Change it to mysql_native_password if you need compatibility with legacy clients.

Performance tuning:

VariableDefaultDescription
MYSQL_MAX_CONNECTIONS151Maximum concurrent client connections
MYSQL_MAX_ALLOWED_PACKET200MMaximum size of a packet or generated string
MYSQL_TABLE_OPEN_CACHE400Open table cache entries across all threads
MYSQL_KEY_BUFFER_SIZE32M or 10% of memoryIndex block buffer size
MYSQL_SORT_BUFFER_SIZE256KSort operation buffer size
MYSQL_READ_BUFFER_SIZE8M or 5% of memorySequential scan buffer size
MYSQL_INNODB_BUFFER_POOL_SIZE32M or 50% of memoryInnoDB table and index cache buffer
MYSQL_INNODB_LOG_FILE_SIZE8M or 15% of memorySize of each log file
MYSQL_INNODB_LOG_BUFFER_SIZE8M or 15% of memoryInnoDB disk log write buffer

Full-text search settings:

VariableDefaultDescription
MYSQL_FT_MIN_WORD_LEN4Minimum word length for a FULLTEXT index
MYSQL_FT_MAX_WORD_LEN20Maximum word length for a FULLTEXT index

Advanced settings:

VariableDefaultDescription
MYSQL_LOWER_CASE_TABLE_NAMES0Controls case sensitivity of table names
MYSQL_AIO1Sets innodb_use_native_aio
MYSQL_BINLOG_FORMATstatementThe replication log format (row, statement)
MYSQL_LOG_QUERIES_ENABLED01 enables query logging
MYSQL_DEFAULTS_FILE/etc/my.cnfPath 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:

VariableDescription
POSTGRESQL_USERThe database user to create
POSTGRESQL_PASSWORDThat user's password
POSTGRESQL_DATABASEThe database to create

Optional environment variables:

VariableDefaultDescription
POSTGRESQL_ADMIN_PASSWORD-The password of the postgres administrator account

Performance tuning:

VariableDefaultDescription
POSTGRESQL_MAX_CONNECTIONS100Maximum client connections
POSTGRESQL_MAX_PREPARED_TRANSACTIONS0Maximum transactions in the prepared state
POSTGRESQL_SHARED_BUFFERS1/4 of memory or 32MShared memory for caching data
POSTGRESQL_EFFECTIVE_CACHE_SIZE1/2 of memory or 128MMemory assumed available for the disk cache

Logging and extensions:

VariableDefaultDescription
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:

VariableDefaultDescription
POSTGRESQL_MIGRATION_REMOTE_HOST-Host name or IP of the migration source
POSTGRESQL_MIGRATION_ADMIN_PASSWORD-The remote postgres administrator password
POSTGRESQL_MIGRATION_IGNORE_ERRORSnoyes ignores errors while importing SQL

Database upgrade:

VariableDescription
POSTGRESQL_UPGRADEUpgrade 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:

VariableDefaultDescription
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.

ItemWhat is appliedIf reversed
Non-root executionThe container runs as UID 185Do not change this with another setting
Management and example web apps removedmanager, host-manager, docs, examples, and ROOT are deleted from the image
Shutdown portNot opened (TOMCAT_SHUTDOWN_PORT defaults to -1)Setting a port makes it accept shutdown commands there
Shutdown command stringRandomized on every startSetting TOMCAT_SHUTDOWN fixes it to that value
AJP connectorNot used (port 8009 is not opened)
Error pagesDo not show stack traces or the Tomcat versionDEBUG=true shows both
Session cookieCarries HttpOnly so scripts cannot read it
Web application privilegesCannot reach the container's internal API (privileged="false")
Client addressRestores 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=true does 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

ItemStatus
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 methodNot allowed, per Tomcat's default configuration
The X-Powered-By response headerNot emitted, per Tomcat's default configuration