Sign inSign up

niceos/openjdk21

By niceos

Updated about 2 months ago

Clean & secure Premium Java 21 latest GA runtime on NiceOS Base — a purpose-built container OS.

Image
Languages & frameworks
2

3.1K

niceos/openjdk21 repository overview

NiceOS OpenJDK 21 Runtime

OpenJDK NiceOS.Container Image Release User Read-only rootfs License

niceos/openjdk21 is a minimal OpenJDK 21 runtime built on NiceOS.Container 13 from the NiceOS openjdk21-jre RPM package.

It is intended for pre-built Java applications running in Docker, Podman, Kubernetes, and other OCI-compatible environments. Operating-system files come from NiceOS RPM packages; NiceSOFT runtime helpers and metadata are stored under /nicesoft.

This is a runtime image. Maven, Gradle, compilers, SSH, and package managers are not included in the final image.

Current release

docker.io/niceos/openjdk21:21.0.11-niceos13-r4

For production, pin the complete release tag or an image digest:

docker pull docker.io/niceos/openjdk21:21.0.11-niceos13-r4

Highlights

  • OpenJDK 21 from the NiceOS openjdk21-jre RPM.
  • NiceOS.Container 13 base filesystem.
  • Minimal final image assembled with FROM scratch.
  • Non-root runtime: UID/GID 1001:1001.
  • Application directory: /app.
  • NiceSOFT runtime layer: /nicesoft.
  • Stable Java path: /nicesoft/java.
  • Container-aware JVM memory options and configurable GC.
  • JFR, AppCDS, custom CA import, and selected _FILE variables.
  • Administrative CLI and runtime inspection.
  • Read-only root filesystem support with writable /tmp.

Runtime contract

/nicesoft                         NiceSOFT helpers and metadata
/nicesoft/java                    stable JAVA_HOME symlink
/nicesoft/bin/nicesoft-java-admin administrative CLI
/nicesoft/scripts/java            entrypoint and runtime scripts
/app                              application working directory
/tmp                              temporary runtime data
/certificates                     recommended custom CA mount point

Default identity:

UID:      1001
GID:      1001
WORKDIR:  /app
JAVA_HOME:/nicesoft/java

This is a native NiceOS.Container image. It does not copy the Bitnami filesystem or shell framework. Compatibility with a particular application image or Helm chart must be implemented and tested in the derived application image.

Quick start

Show the Java version:

docker run --rm \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4

Run an executable JAR:

docker run --rm \
  -v "$PWD/app.jar:/app/app.jar:ro" \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4 \
  -jar /app/app.jar

Run with application arguments:

docker run --rm \
  -p 8080:8080 \
  -v "$PWD/app.jar:/app/app.jar:ro" \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4 \
  -jar /app/app.jar \
  --server.port=8080 \
  --spring.profiles.active=prod

Run a main class:

docker run --rm \
  -v "$PWD/application:/app:ro" \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4 \
  -cp '/app:/app/*' com.example.Main arg1 arg2

Run a command directly:

docker run --rm \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4 \
  /bin/sh -c 'java -version && id'
Important r4 command behavior

When the first argument starts with -, the entrypoint runs:

/nicesoft/java/bin/java <generated JVM defaults> <JAVA_OPTS> <arguments>

When the first argument is exactly java, the command is executed as supplied and generated NiceOS JVM defaults are not inserted. Add all required JVM options explicitly in that form.

Release r4 defines CMD ["java", "-version"]. Therefore, for reliable application startup, use CMD ["-jar", "/app/app.jar"] in a derived image or pass -jar /app/app.jar explicitly.

Build an application image

FROM docker.io/niceos/openjdk21:21.0.11-niceos13-r4

COPY --chown=1001:1001 app.jar /app/app.jar

EXPOSE 8080

CMD ["-jar", "/app/app.jar"]

Build and run:

docker build -t example/my-java-app:1.0.0 .
docker run --rm -p 8080:8080 example/my-java-app:1.0.0

Build the application in a separate stage or CI environment. Copy only runtime artifacts into the final image.

Entrypoint flow

Before starting the selected command, the entrypoint:

  1. Loads supported _FILE variables.
  2. Prints the banner unless disabled.
  3. Validates Java, /app, and writable JAVA_TMP_DIR.
  4. Imports mounted custom CA certificates when enabled.
  5. Generates default JVM options.
  6. Sources an optional pre-start script.
  7. Starts the selected command.

Without positional arguments, the implemented application selection order is:

  1. JAVA_COMMAND
  2. JAVA_MAIN_JAR
  3. /app/app.jar, when present
  4. JAVA_MAIN_CLASS
  5. java -version

Environment variables

Runtime and logging
VariableDefaultPurpose
NICESOFT_APP_NAMENiceOS OpenJDK 21 RuntimeName shown in runtime messages.
NICESOFT_BANNERtrueEnable startup banner.
NICESOFT_COLORautoauto, always, or never.
NICESOFT_LOG_ENABLEDtrueEnable runtime logs.
NICESOFT_LOG_LEVELinfotrace, debug, info, warn, error, off.
NICESOFT_LOG_FORMATtexttext or json.
NICESOFT_LOG_TIMESTAMPtrueAdd UTC timestamps to formatted Java logs.
NICESOFT_LOG_JAVAtruePipe Java stdout/stderr through nicesoft-log.
NICESOFT_DEBUGfalseEnable shell tracing and debug logs. Do not use with secrets.

Legacy aliases NICEOS_BANNER, NICEOS_COLOR, NICEOS_LOG_LEVEL, and NICEOS_LOG_FORMAT are accepted when the corresponding NICESOFT_* variable is not set.

Application startup
VariableDefaultPurpose
JAVA_HOME/nicesoft/javaStable Java runtime path.
JAVA_APP_DIR/appApplication directory.
JAVA_TMP_DIR/tmpWritable Java temporary directory.
JAVA_MAIN_JARunsetExecutable JAR for environment-driven startup.
JAVA_MAIN_CLASSunsetMain class for environment-driven startup.
JAVA_CLASSPATH/app:/app/*Classpath for JAVA_MAIN_CLASS.
JAVA_COMMANDunsetComplete shell command for environment-driven startup.
JAVA_PRESTART_SCRIPTunsetTrusted shell file sourced before startup.
JAVA_OPTSunsetAdditional JVM options.
JAVA_ARGSunsetApplication arguments.
JAVA_TOOL_OPTIONSunsetStandard JVM environment variable.
JDK_JAVA_OPTIONSunsetStandard JDK launcher environment variable.
Memory and JVM defaults
VariableDefaultResult
JAVA_ENABLE_CONTAINER_SUPPORTtrue-XX:+UseContainerSupport
JAVA_MAX_RAM_PERCENTAGE75-XX:MaxRAMPercentage=<value>
JAVA_INITIAL_RAM_PERCENTAGEunset-XX:InitialRAMPercentage=<value>
JAVA_MIN_RAM_PERCENTAGEunset-XX:MinRAMPercentage=<value>
JAVA_ENABLE_HEADLESStrue-Djava.awt.headless=true
JAVA_FILE_ENCODINGUTF-8-Dfile.encoding=UTF-8
JAVA_USER_TIMEZONEUTC-Duser.timezone=UTC
JAVA_SECURITY_EGDfile:/dev/urandom-Djava.security.egd=...

Example:

docker run --rm \
  --memory=1g \
  -e JAVA_MAX_RAM_PERCENTAGE=65 \
  -e JAVA_GC=g1 \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4 \
  -XshowSettings:vm -version

JAVA_MAX_RAM_PERCENTAGE only controls the Java heap. Keep headroom for metaspace, thread stacks, direct buffers, native libraries, and operating-system overhead.

Garbage collectors

Set JAVA_GC to:

ValueJVM option
autoJVM default
g1, g1gc-XX:+UseG1GC
zgc, z-XX:+UseZGC
shenandoah-XX:+UseShenandoahGC
serial-XX:+UseSerialGC
parallel-XX:+UseParallelGC
none, disabled, offno GC option added

Secret files

Supported _FILE pairs:

JAVA_OPTS / JAVA_OPTS_FILE
JAVA_TOOL_OPTIONS / JAVA_TOOL_OPTIONS_FILE
JDK_JAVA_OPTIONS / JDK_JAVA_OPTIONS_FILE
JAVA_ARGS / JAVA_ARGS_FILE
JAVA_MAIN_JAR / JAVA_MAIN_JAR_FILE
JAVA_MAIN_CLASS / JAVA_MAIN_CLASS_FILE
JAVA_COMMAND / JAVA_COMMAND_FILE
JAVA_TRUSTSTORE_PATH / JAVA_TRUSTSTORE_PATH_FILE
JAVA_TRUSTSTORE_PASSWORD / JAVA_TRUSTSTORE_PASSWORD_FILE
NICESOFT_JAVA_TRUSTSTORE_PASSWORD / NICESOFT_JAVA_TRUSTSTORE_PASSWORD_FILE

Do not set a direct variable and its _FILE counterpart together. The container exits when both are set or when the referenced file is unreadable.

docker run --rm \
  -v "$PWD/password:/run/secrets/password:ro" \
  -e JAVA_TRUSTSTORE_PASSWORD_FILE=/run/secrets/password \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4

NICESOFT_DEBUG=true enables shell tracing in r4 and may expose command arguments or environment values. Do not enable it in production when secrets are present.

Custom CA certificates

Mount .crt, .cer, or .pem files under /certificates:

docker run --rm \
  -v "$PWD/company-root-ca.crt:/certificates/company-root-ca.crt:ro" \
  -v "$PWD/app.jar:/app/app.jar:ro" \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4 \
  -jar /app/app.jar

With NICESOFT_ENABLE_CERT_IMPORT=auto, the runtime copies the system Java truststore to a writable target under /tmp, imports mounted certificates, and adds the matching javax.net.ssl.trustStore properties.

VariableDefault
NICESOFT_ENABLE_CERT_IMPORTauto
NICESOFT_CA_CERTS_DIR/certificates
NICESOFT_JAVA_TRUSTSTORE/tmp/nicesoft-java-cacerts
NICESOFT_JAVA_TRUSTSTORE_PASSWORDchangeit

Inspect the effective truststore:

docker run --rm \
  -v "$PWD/ca:/certificates:ro" \
  --entrypoint nicesoft-java-admin \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4 \
  truststore

JFR and AppCDS

Enable Java Flight Recorder:

docker run --rm \
  -e JAVA_ENABLE_JFR=true \
  -e JAVA_JFR_FILENAME=/tmp/application.jfr \
  -e JAVA_JFR_SETTINGS=profile \
  -v "$PWD/app.jar:/app/app.jar:ro" \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4 \
  -jar /app/app.jar

JFR variables: JAVA_ENABLE_JFR, JAVA_JFR_FILENAME, JAVA_JFR_SETTINGS, JAVA_JFR_DELAY, and JAVA_JFR_DURATION.

Use a pre-generated AppCDS archive:

docker run --rm \
  -e JAVA_ENABLE_APP_CDS=true \
  -e JAVA_APP_CDS_ARCHIVE=/app/app-cds.jsa \
  -v "$PWD/app.jar:/app/app.jar:ro" \
  -v "$PWD/app-cds.jsa:/app/app-cds.jsa:ro" \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4 \
  -jar /app/app.jar

JAVA_ENABLE_APP_CDS=auto uses the configured archive only when it exists.

Logging

Runtime messages use NICESOFT_LOG_LEVEL, NICESOFT_LOG_FORMAT, and related variables.

Release r4 defaults to NICESOFT_LOG_JAVA=true, which transforms Java stdout/stderr through nicesoft-log. Disable it when the application already emits JSON or requires exact stdout/stderr:

docker run --rm \
  -e NICESOFT_LOG_JAVA=false \
  -v "$PWD/app.jar:/app/app.jar:ro" \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4 \
  -jar /app/app.jar

For most production applications with their own logging format, NICESOFT_LOG_JAVA=false is recommended.

Healthcheck

The built-in helper executes JAVA_HEALTH_COMMAND; its default is java -version.

This verifies the Java runtime, not application health or readiness. Derived production images should replace it with an application-specific probe.

HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
  CMD ["/bin/sh", "-c", "exec /app/healthcheck.sh"]

Values none, disabled, off, or false make the helper return success without running a check.

Read-only root filesystem

docker run --rm \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,nodev,size=256m \
  -e NICESOFT_LOG_JAVA=false \
  -v "$PWD/app.jar:/app/app.jar:ro" \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4 \
  -jar /app/app.jar

Mount writable volumes for every path used by the application for mutable or persistent data. The base runtime requires a writable JAVA_TMP_DIR.

Docker Compose

services:
  app:
    image: docker.io/niceos/openjdk21:21.0.11-niceos13-r4
    user: "1001:1001"
    read_only: true
    command: ["-jar", "/app/app.jar"]
    tmpfs:
      - /tmp:rw,noexec,nosuid,nodev,size=256m
    environment:
      JAVA_MAX_RAM_PERCENTAGE: "65"
      JAVA_USER_TIMEZONE: Europe/Amsterdam
      NICESOFT_LOG_JAVA: "false"
    volumes:
      - ./app.jar:/app/app.jar:ro
    ports:
      - "8080:8080"
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL

Kubernetes

apiVersion: apps/v1
kind: Deployment
metadata:
  name: java-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: java-app
  template:
    metadata:
      labels:
        app: java-app
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1001
        runAsGroup: 1001
        fsGroup: 1001
      containers:
        - name: app
          image: docker.io/niceos/openjdk21:21.0.11-niceos13-r4
          args: ["-jar", "/app/app.jar"]
          env:
            - name: JAVA_MAX_RAM_PERCENTAGE
              value: "65"
            - name: NICESOFT_LOG_JAVA
              value: "false"
          resources:
            requests:
              cpu: 100m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 1Gi
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop: ["ALL"]
            seccompProfile:
              type: RuntimeDefault
          volumeMounts:
            - name: app
              mountPath: /app
              readOnly: true
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: app
          configMap:
            name: java-app-files
        - name: tmp
          emptyDir: {}

For real applications, use an application image containing the JAR instead of storing a large artifact in a ConfigMap. Configure application-specific startup, readiness, and liveness probes.

Administrative CLI

The image provides:

/nicesoft/bin/nicesoft-java-admin
/nicesoft/bin/niceos-java-admin

Common commands:

docker run --rm --entrypoint nicesoft-java-admin IMAGE inspect
docker run --rm --entrypoint nicesoft-java-admin IMAGE opts
docker run --rm --entrypoint nicesoft-java-admin IMAGE truststore
docker run --rm --entrypoint nicesoft-java-admin IMAGE version

Replace IMAGE with a valid tag such as docker.io/niceos/openjdk21:21.0.11-niceos13-r4.

Tag policy

Tag format

The canonical release tag is:

<java-version>-niceos<niceos-stream>-r<revision>

Example:

21.0.11-niceos13-r4

Components:

  • 21.0.11: OpenJDK runtime version.
  • niceos13: NiceOS.Container base stream.
  • r4: NiceOS image revision for that exact Java version and base stream.
Published tag classes
TagTypeMeaning
21.0.11-niceos13-r4immutableExact release. Recommended for production.
21.0.11-niceos13movingLatest revision for this Java version and NiceOS stream.
21.0.11movingLatest approved image for this Java patch version.
21movingLatest supported OpenJDK 21 release.
latestmovingLatest default release in this repository.

Moving tags are convenience aliases and may change without modifying their tag name.

Production rule

Use one of these:

21.0.11-niceos13-r4
<registry>/<image>@sha256:<digest>

Do not rely on latest, 21, or other moving aliases for reproducible production deployments.

Revision rules

The rN revision increases when the Java version and NiceOS stream stay unchanged but the image must be rebuilt because of:

  • updated NiceOS RPM dependencies;
  • security fixes in the base filesystem;
  • runtime script or entrypoint fixes;
  • metadata, certificate, permission, or packaging corrections;
  • rebuilt artifacts or build-pipeline changes that affect the final image;
  • documentation changes shipped as part of a new image release.

Examples:

21.0.11-niceos13-r4 -> 21.0.11-niceos13-r5
21.0.11-niceos13-r5 -> 21.0.12-niceos13-r1
21.0.12-niceos13-r3 -> 21.0.12-niceos14-r1

A new Java version or a new NiceOS stream resets the image revision to r1.

Immutability

A complete release tag must never be overwritten. Once published, 21.0.11-niceos13-r4 must always resolve to the same image content. Corrections require a new rN tag.

Old immutable tags remain available for rollback and reproducible deployments, subject to registry retention and security policy.

Update policy

NiceOS OpenJDK images follow the supported OpenJDK 21 line and the active NiceOS.Container release stream.

Update checks

The following are checked at least weekly:

  • upstream OpenJDK releases and security updates;
  • NiceOS RPM and base-image updates;
  • known vulnerabilities affecting shipped runtime components;
  • runtime contract, entrypoint, and build-pipeline fixes.

Critical security updates may be released outside the normal schedule.

Java updates

When a new supported OpenJDK 21 patch or security release is approved:

  1. the NiceOS RPM is built and validated;
  2. the container is rebuilt from the approved RPM;
  3. tests and runtime checks are executed;
  4. a new immutable tag starts at r1;
  5. moving aliases are updated only after successful validation.

A Java version change produces a new versioned tag; the previous immutable release is not modified.

NiceOS and image-only updates

When Java stays unchanged but the NiceOS base, RPM dependencies, scripts, metadata, or container contract require an update, the image revision increases:

21.0.11-niceos13-r4 -> 21.0.11-niceos13-r5

A new NiceOS base stream produces a new tag and resets the revision:

21.0.11-niceos13-r5 -> 21.0.11-niceos14-r1
Alias promotion

Moving aliases are updated only after the candidate image passes required build and acceptance checks. Promotion order should be:

  1. publish the immutable full tag;
  2. verify manifest, metadata, startup, non-root execution, and runtime tests;
  3. publish or update the Java-version and NiceOS-stream aliases;
  4. update 21 and latest only when the release is the approved default.
Rollback

Rollback is performed by redeploying a previous immutable tag or digest. Moving aliases are not rollback points.

Security

  • Runs as UID/GID 1001:1001 by default.
  • No package manager, compiler, build system, or SSH server in the final image.
  • Supports readOnlyRootFilesystem: true with writable /tmp.
  • Does not require Linux capabilities or privileged mode for normal operation.
  • Compatible with allowPrivilegeEscalation: false, dropped capabilities, and RuntimeDefault seccomp.
  • Secrets can be loaded from files through supported _FILE variables.
  • Custom truststores are created in writable runtime storage instead of modifying the system Java installation.

Derived applications remain responsible for their own dependencies, writable paths, network exposure, probes, and application-level security.

Known r4 limitations

  • Default CMD ["java", "-version"] means plain docker run IMAGE does not use environment-driven application selection.
  • A first argument equal to java bypasses generated NiceOS JVM defaults.
  • NICESOFT_LOG_JAVA=true transforms application stdout/stderr and keeps the shell wrapper involved in process handling.
  • The default healthcheck runs java -version; it is not an application readiness check.
  • NICESOFT_DEBUG=true enables shell tracing and may expose sensitive values.
  • Arbitrary-UID/OpenShift compatibility is not guaranteed by the current runtime contract; the supported default identity is 1001:1001.

Build and test

Build:

docker build \
  --build-arg IMAGE_VERSION=21.0.11-niceos13-r4 \
  --build-arg VCS_REF="$(git rev-parse --short HEAD)" \
  --build-arg BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
  -t docker.io/niceos/openjdk21:21.0.11-niceos13-r4 .

Run repository checks:

./tests/release-check.sh
docker build -t niceos/openjdk21:test .
IMAGE=niceos/openjdk21:test ./tests/acceptance.sh

OCI metadata

The image publishes standard OCI labels, including title, description, version, source, revision, creation time, vendor, licenses, base image, and documentation URL.

Inspect labels:

docker image inspect \
  docker.io/niceos/openjdk21:21.0.11-niceos13-r4 \
  --format '{{json .Config.Labels}}'

Support and source

When reporting a problem, include the complete image tag or digest, architecture, container runtime, startup command, relevant environment variables with secrets removed, and logs.

License

OpenJDK is distributed under GPL-2.0-only WITH Classpath-exception-2.0. NiceSOFT helper scripts and supporting files in this repository are licensed under Apache License 2.0 unless stated otherwise.

Tag summary

Content type

Image

Digest

sha256:19cc986fe

Size

238.4 MB

Last updated

about 2 months ago

docker pull niceos/openjdk21