Skip to content

add APPLICATION_PROPERTIES env var for Spring Boot runtime tuning#762

Open
WMP wants to merge 1 commit intombentley:masterfrom
WMP:feature/application-properties-injection
Open

add APPLICATION_PROPERTIES env var for Spring Boot runtime tuning#762
WMP wants to merge 1 commit intombentley:masterfrom
WMP:feature/application-properties-injection

Conversation

@WMP
Copy link
Copy Markdown
Contributor

@WMP WMP commented Apr 18, 2026

Problem

The Omada Controller v5.x+ is built on Spring Boot with an embedded Tomcat server. Its default configuration allocates large thread pools (Tomcat max 200 threads, unbounded async executor) that are sized for general use — not for memory-constrained environments like Raspberry Pi, small VMs, or Kubernetes pods with tight resource limits. There was no way to tune these without rebuilding the image.

Key observation

The startup classpath is -cp ".../lib/*:.../properties". Spring Boot automatically loads application.properties from the classpath root. The properties/ directory does not currently contain an application.properties file, so adding one introduces no conflicts.

Solution

A new APPLICATION_PROPERTIES environment variable. Its content is written verbatim to /opt/tplink/EAPController/properties/application.properties at startup (before the JVM launches), picked up automatically by Spring Boot.

What you can tune:

  • server.tomcat.threads.max / min-spare — HTTP worker thread pool
  • spring.task.execution.pool.* — async @Async task executor
  • spring.task.scheduling.pool.size@Scheduled task pool
  • logging.level.* — log verbosity

Example (Kubernetes):

extraEnvVars:
  APPLICATION_PROPERTIES: |
    server.tomcat.threads.max=50
    server.tomcat.threads.min-spare=5
    spring.task.execution.pool.core-size=5
    spring.task.execution.pool.max-size=20
    spring.task.scheduling.pool.size=2
    logging.level.root=WARN
    logging.level.com.tplink=INFO

Files changed

  • entrypoint-unified.sh — new inject_application_properties() function + env var init + call from common_setup_and_validation()
  • README.md — new table row + reference from the Low Resource Systems section
  • docs/spring-boot-tuning.mdnew file with full reference: all tuneable properties, defaults, descriptions, and a complete memory-constrained example

Notes

  • No-op when APPLICATION_PROPERTIES is unset — zero behaviour change for existing users
  • Written as root before gosu switch — works in rootless mode
  • Only effective for v5.x and above (Spring Boot base); silently ignored on v4.x
  • Marked as best-effort/unofficial in the docs — TP-Link does not document which Spring Boot properties they honour

Verification

  1. Run with APPLICATION_PROPERTIES="server.tomcat.threads.max=50"
  2. Log shows: INFO: APPLICATION_PROPERTIES set; writing Spring Boot properties to .../application.properties
  3. docker exec <container> cat /opt/tplink/EAPController/properties/application.properties → shows the content
  4. Without the env var → file is not created, behaviour unchanged

🤖 Generated with Claude Code

The Omada Controller (v5.x+) is built on Spring Boot. Its classpath
includes /opt/tplink/EAPController/properties, so Spring Boot will
automatically load an application.properties file placed there.

inject_application_properties() writes the content of APPLICATION_PROPERTIES
to that file before the JVM starts, enabling users to tune Spring Boot
internals (Tomcat thread pool, async executors, logging) without rebuilding
the image or overriding the CMD.

- No-op when APPLICATION_PROPERTIES is unset
- File is written as root before gosu switch; compatible with rootless mode
- Does not affect omada.properties (separate file, separate purpose)
- Adds docs/spring-boot-tuning.md with recommended settings and examples

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mbentley
Copy link
Copy Markdown
Owner

I will take a look at this when I have time to properly review it, hopefully tomorrow.

Copy link
Copy Markdown
Owner

@mbentley mbentley left a comment

Choose a reason for hiding this comment

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

Clever overall solution! Just a couple things to review and please double check my logic on this. And of course it'll need to be rebased to deal with the conflicts from #761

The `APPLICATION_PROPERTIES` environment variable allows you to inject arbitrary Spring Boot properties
into the controller without rebuilding the image or overriding the `CMD`.

> **Disclaimer**: This is an unofficial tuning mechanism. TP-Link does not document or guarantee which
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Could you use one of the Blockquote styles for this disclaimer to bring more attention to this statement?

Comment thread entrypoint-unified.sh

PROPS_FILE="/opt/tplink/EAPController/properties/application.properties"
echo "INFO: APPLICATION_PROPERTIES set; writing Spring Boot properties to ${PROPS_FILE}"
printf '%s\n' "${APPLICATION_PROPERTIES}" > "${PROPS_FILE}"
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

In non-rootless mode, the script runs as root at this point and gosu only executes at the very end. Double check my logic but the file is created owned by root:root with mode 640. The omada controller process (launched via exec gosu "${PUSERNAME}") is not in the root group, so it cannot read the file. Spring Boot will silently finds no application.properties and the feature does nothing.

So if I am right, the fix that would be easiest would probably be to add the same chown in inject_application_properties(), guarded for rootless.

@@ -0,0 +1,190 @@
# Spring Boot Tuning
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Instead of putting this in a separate docs/ directory, could we just move this to the root of the project? I know it's starting to get a bit unorganized but that might be a future me problem to address properly.

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.

2 participants