Skip to content

feat: add slim Docker image variant without embedded postgres#3610

Open
dkrizan wants to merge 1 commit intomainfrom
dkrizan/docker-slim-image
Open

feat: add slim Docker image variant without embedded postgres#3610
dkrizan wants to merge 1 commit intomainfrom
dkrizan/docker-slim-image

Conversation

@dkrizan
Copy link
Copy Markdown
Member

@dkrizan dkrizan commented Apr 17, 2026

Summary

Adds a new tolgee/tolgee:slim Docker image variant built on eclipse-temurin:21-jre-alpine, alongside the existing tolgee/tolgee image. No breaking changes — the default image and its behavior are untouched.

Why: the current tolgee/tolgee image uses FROM postgres:… so that postgres-autostart.mode: EMBEDDED can spawn a postgres instance inside the container (nice for docker run evaluations). Production deployments run Tolgee against an external, managed postgres and don't use the embedded server — but still pay the cost of all the postgres binaries and their transitive OS packages in the image. The slim variant carries only what the application actually needs.

What ships

Tag Meaning
tolgee/tolgee:slim Floating, tracks the latest slim build
tolgee/tolgee:<version>-slim Versioned slim, released alongside each <version>

The slim image:

  • Is based on eclipse-temurin:21-jre-alpine (JRE only, no JDK).
  • Installs only bash (needed by cmd.sh).
  • Downloads the OpenTelemetry Java agent the same way the default image does.
  • Sets TOLGEE_POSTGRES_AUTOSTART_ENABLED=false by default — slim doesn't bundle postgres, so the autostart machinery would fail; the variant must be run against an external database.
  • Uses the same cmd.sh, healthcheck, exposed port, and /data volume as the default image.

The default Dockerfile and tolgee/tolgee tag are unchanged.

Changes

  • docker/app/Dockerfile.slim — new Dockerfile for the slim variant.
  • gradle/docker.gradle — adds dockerSlim (local build) and dockerPublishSlim (multi-arch CI publish).
  • .github/workflows/release.yml — release job now also runs dockerPublishSlim for both the versioned tag and latest.

Relationship to #3193

This revives the idea from #3193 (stale). That PR initially swapped the default image to non-postgres, then (per review feedback) switched to an additive Dockerfile.lite. This PR takes the additive approach through to the finish line — adds the CI plumbing, aligns with the OTEL changes made since that PR was opened, and uses slim (standard Docker-world naming) instead of lite.

Thanks to @transparentChange for the original work. Leaving #3193 open for now so the discussion there remains linked.

Test plan

  • ./gradlew dockerSlim builds the image locally.
  • docker run --rm -e SPRING_DATASOURCE_URL=... tolgee/tolgee:slim starts the app against an external postgres.
  • Health check endpoint responds on /actuator/health.
  • OTEL agent attaches when OTEL_JAVAAGENT_ENABLED=true.
  • CI release workflow publishes both :${version}-slim and :slim tags.
  • Default tolgee/tolgee image continues to build and publish unchanged.

Follow-ups (out of scope)

  • Update Docker Hub description with guidance on picking between the two variants.
  • Documentation update on the Tolgee docs site.
  • Phase 2 (swapping the default to slim at a future major version) is tracked internally; not pursued here.

🤖 Generated with Claude Code

The existing `tolgee/tolgee` image is based on `postgres` to support
`postgres-autostart.mode: EMBEDDED`. For production deployments against an
external database, the postgres server binaries and their transitive OS
packages aren't used at runtime.

Add a slim variant built from `eclipse-temurin:21-jre-alpine` that carries
only what the application needs. Published alongside the existing image:

  - tolgee/tolgee:slim          (floating, tracks latest)
  - tolgee/tolgee:<version>-slim

The slim image sets `TOLGEE_POSTGRES_AUTOSTART_ENABLED=false` by default
since embedded postgres isn't available in this variant; it must be run
against an external database.

Revives the approach from PR #3193 (stale). Current Dockerfile and default
`tolgee/tolgee` tag are unchanged — this is purely additive.

Co-authored-by: Matthew Sekirin <matthew.sekirin@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 17, 2026

📝 Walkthrough

Walkthrough

Introduces a new "slim" Docker image variant for Tolgee by adding Dockerfile.slim (Alpine Linux with Java 21 JRE), creating Gradle tasks dockerSlim and dockerPublishSlim for local and CI builds, and expanding the release workflow with dockerPublishSlim invocations to build and publish the slim artifact with registry caching.

Changes

Cohort / File(s) Summary
Docker Image Configuration
docker/app/Dockerfile.slim
New Alpine-based Java 21 JRE Dockerfile with bash, port 8080 exposure, /data volume, Docker Spring profile, OpenTelemetry agent download via build-arg, and health check via /actuator/health endpoint.
Gradle Build Tasks
gradle/docker.gradle
Added dockerSlim task for local single-platform builds and dockerPublishSlim task for CI multi-platform (linux/arm64,linux/amd64) builds with registry caching and dynamic version-based tagging (:slim or :${VERSION}-slim).
Release Workflow
.github/workflows/release.yml
Added two ./gradlew dockerPublishSlim invocations with DOCKER_CACHE_FROM=type=registry,ref=tolgee/tolgee:slim and VERSION=latest parameters, both gated by existing VERSION != '' condition.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • gabrielshanahan
  • JanCizmar

Poem

🐰 A nimble Docker variant hops into view,
Slim and fleet on Alpine so true,
With OpenTelemetry's watchful eye,
Multi-platform builds reach the sky!
Tolgee grows lighter, faster, and spry!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: addition of a slim Docker image variant without embedded Postgres, which is the primary objective of this PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dkrizan/docker-slim-image

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
gradle/docker.gradle (1)

95-123: Consider extracting shared publish logic to reduce duplication.

dockerPublishSlim is nearly identical to dockerPublish — the only differences are the tag mapping and the -f Dockerfile.slim flag. If a third variant is ever added, the duplication will compound. A small helper (e.g., a closure taking dockerfile and a tagResolver) would DRY this up without changing behavior.

♻️ Sketch
def buildxPublish = { String dockerfile, Closure<String> tagResolver ->
    def version = System.getenv('VERSION') ?: 'latest'
    def imageName = System.getenv('DOCKER_IMAGE') ?: 'tolgee/tolgee'
    def tag = tagResolver(imageName, version)
    def cacheFrom = System.getenv('DOCKER_CACHE_FROM')
    def cacheTo = System.getenv('DOCKER_CACHE_TO')

    def cmd = ["docker", "buildx", "build", "."]
    if (dockerfile) cmd += ["-f", dockerfile]
    cmd += ["-t", tag,
            "--build-arg", "OTEL_AGENT_VERSION=${opentelemetryJavaagentVersion}",
            "--platform", "linux/arm64,linux/amd64",
            "--push"]
    if (cacheFrom) cmd += ["--cache-from", cacheFrom]
    if (cacheTo)   cmd += ["--cache-to", cacheTo]

    exec { workingDir dockerPath; commandLine cmd }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gradle/docker.gradle` around lines 95 - 123, dockerPublishSlim duplicates
dockerPublish; extract the shared logic into a reusable closure (e.g.,
buildxPublish) that accepts a dockerfile name and a tag-resolver closure, move
the environment reads (VERSION, DOCKER_IMAGE, DOCKER_CACHE_FROM,
DOCKER_CACHE_TO) and common args (buildx build, OTEL_AGENT_VERSION build-arg,
platforms, push) into that closure, then replace the dockerPublishSlim task body
to call buildxPublish("Dockerfile.slim", { imageName, version -> /* slim tag
logic */ }) and likewise call it from dockerPublish with the default Dockerfile
and tag resolver, preserving the existing tag mapping and cache-from/cache-to
handling.
docker/app/Dockerfile.slim (1)

34-35: Pin the OTEL agent JAR by checksum (optional hardening).

The agent JAR is fetched over HTTPS from GitHub Releases without integrity verification. A checksum check (the project publishes .sha256/.asc alongside the jar) protects the image build from upstream tampering or transient corruption. Same concern applies to the non-slim Dockerfile; feel free to track separately and apply uniformly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker/app/Dockerfile.slim` around lines 34 - 35, The Dockerfile.slim RUN
step that downloads opentelemetry-javaagent.jar using OTEL_AGENT_VERSION must be
hardened by verifying the artifact checksum: fetch the corresponding .sha256 (or
.sha256sum/.asc) published alongside the jar (using the same
OTEL_AGENT_VERSION), validate the downloaded /app/opentelemetry-javaagent.jar
with sha256sum (or gpg verify if using .asc), and fail the build if the
verification fails; update the RUN instruction that does wget to also wget the
checksum (or signature) and perform the verification before keeping the jar, and
apply the same change to the non-slim Dockerfile equivalent so both images are
protected from tampered or corrupted releases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docker/app/Dockerfile.slim`:
- Around line 1-3: The Dockerfile runs as root by default; create and switch to
a non-root user (e.g., user name "tolgee") by adding a user/group creation step,
ensure ownership of runtime directories like /data is changed (chown) so the new
user can write, copy application files as that non-root user (or adjust
ownership after copy), and set USER tolgee before the ENTRYPOINT to run the
container non-root; update any steps that assume root accordingly.

---

Nitpick comments:
In `@docker/app/Dockerfile.slim`:
- Around line 34-35: The Dockerfile.slim RUN step that downloads
opentelemetry-javaagent.jar using OTEL_AGENT_VERSION must be hardened by
verifying the artifact checksum: fetch the corresponding .sha256 (or
.sha256sum/.asc) published alongside the jar (using the same
OTEL_AGENT_VERSION), validate the downloaded /app/opentelemetry-javaagent.jar
with sha256sum (or gpg verify if using .asc), and fail the build if the
verification fails; update the RUN instruction that does wget to also wget the
checksum (or signature) and perform the verification before keeping the jar, and
apply the same change to the non-slim Dockerfile equivalent so both images are
protected from tampered or corrupted releases.

In `@gradle/docker.gradle`:
- Around line 95-123: dockerPublishSlim duplicates dockerPublish; extract the
shared logic into a reusable closure (e.g., buildxPublish) that accepts a
dockerfile name and a tag-resolver closure, move the environment reads (VERSION,
DOCKER_IMAGE, DOCKER_CACHE_FROM, DOCKER_CACHE_TO) and common args (buildx build,
OTEL_AGENT_VERSION build-arg, platforms, push) into that closure, then replace
the dockerPublishSlim task body to call buildxPublish("Dockerfile.slim", {
imageName, version -> /* slim tag logic */ }) and likewise call it from
dockerPublish with the default Dockerfile and tag resolver, preserving the
existing tag mapping and cache-from/cache-to handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bae4c2b4-b78c-46bb-821a-5f6e662feb93

📥 Commits

Reviewing files that changed from the base of the PR and between ff89c7b and 615ddf6.

📒 Files selected for processing (3)
  • .github/workflows/release.yml
  • docker/app/Dockerfile.slim
  • gradle/docker.gradle

Comment thread docker/app/Dockerfile.slim
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant