Skip to content
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
89de2bd
refactor(docker): consolidate JVM options and improve dynamic memory …
balazs-szucs Mar 13, 2026
e582868
Merge branch 'main' into docker-stuff
balazs-szucs Mar 13, 2026
b956ba5
fix(docker): correct JVM options for proper configuration
balazs-szucs Mar 13, 2026
2978fa0
fix(docker): standardize JVM options across Dockerfiles and init script
balazs-szucs Mar 13, 2026
4c7efc5
fix(deps): update googleJavaFormatVersion to 1.35.0
balazs-szucs Mar 14, 2026
36ea762
Merge branch 'main' into docker-stuff
balazs-szucs Mar 14, 2026
dad358b
feat(docker): enhance JVM options and add security JAR setup in entry…
balazs-szucs Mar 15, 2026
f04cc7d
refactor(docker): simplify init scripts and enhance environment setup
balazs-szucs Mar 15, 2026
8edf2de
fix(docker): adjust Jetty connection idle timeout and update thread s…
balazs-szucs Mar 15, 2026
8e61c0d
fix(docker): remove outdated comments and adjust Jetty connection idl…
balazs-szucs Mar 15, 2026
5392127
Merge branch 'main' into docker-stuff
balazs-szucs May 30, 2026
69087a8
feat: implement on-demand unoserver management and optimize memory fo…
balazs-szucs May 30, 2026
c034f2d
chore: force eager initialization for startup components and update t…
balazs-szucs Jun 1, 2026
4ed5e4f
refactor: remove lazy initialization configuration from application p…
balazs-szucs Jun 1, 2026
d183117
chore: exclude uno-last-used temporary files from output in test script
balazs-szucs Jun 1, 2026
dd1f0f4
Merge branch 'main' into docker-stuff
balazs-szucs Jun 1, 2026
f2d1da6
chore: update file exclusion patterns in test script and increase log…
balazs-szucs Jun 1, 2026
fe50ec6
fix: update post-auth navigation verification and increase stabilizat…
balazs-szucs Jun 1, 2026
a70b93c
feat: add default test license key bypass and apply code formatting t…
balazs-szucs Jun 1, 2026
36ececf
refactor: reformat log message for default test license key detection
balazs-szucs Jun 1, 2026
1ae7576
Merge branch 'main' into docker-stuff
balazs-szucs Jun 1, 2026
6da2a87
Revert testing stuff
balazs-szucs Jun 1, 2026
3eb0a28
chore: exclude transient LibreOffice, X11, and dconf files from tempo…
balazs-szucs Jun 2, 2026
6e6161d
chore: exclude additional persistent temporary file patterns from tes…
balazs-szucs Jun 2, 2026
b76c24d
Merge branch 'main' into docker-stuff
balazs-szucs Jun 11, 2026
5aef240
Merge branch 'main' into docker-stuff
Frooodle Aug 1, 2026
300b8a9
Merge branch 'main' into docker-stuff
balazs-szucs Aug 6, 2026
dce0dcd
fix: add local endpoint detection to UnoServerPool and skip demand fi…
balazs-szucs Aug 6, 2026
dc4640e
Merge branch 'main' into docker-stuff
balazs-szucs Aug 11, 2026
c70497c
Merge branch 'main' into docker-stuff
balazs-szucs Aug 13, 2026
5871b48
Merge branch 'main' into docker-stuff
balazs-szucs Aug 14, 2026
e7a4be0
Merge branch 'main' into docker-stuff
balazs-szucs Aug 14, 2026
ac5f4b0
Merge branch 'main' into docker-stuff
balazs-szucs Aug 24, 2026
385d6e7
Merge branch 'main' into docker-stuff
balazs-szucs Aug 26, 2026
2878692
Merge branch 'main' into docker-stuff
balazs-szucs Aug 26, 2026
b119181
chore: update dynamic memory and metaspace configuration defaults in …
balazs-szucs Aug 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.LockSupport;

import io.github.pixee.security.BoundedLineReader;

Expand Down Expand Up @@ -202,6 +203,14 @@ public ProcessExecutorResult runCommandWithOutputHandling(
UnoServerPool.UnoServerLease unoLease = null;
boolean useSemaphore = true;
List<String> commandToRun = command;

// Signal the on-demand manager to start unoserver if needed.
// Must happen before acquiring the semaphore/lease so the manager has
// time to spin up soffice while we wait.
if (processType == Processes.LIBRE_OFFICE) {
Comment thread
aikido-autofix[bot] marked this conversation as resolved.
signalUnoServerDemand();
}

if (shouldUseUnoServerPool(command)) {
try {
unoLease = unoServerPool.acquireEndpoint(timeoutDuration, TimeUnit.MINUTES);
Expand Down Expand Up @@ -537,6 +546,59 @@ private void validateCommand(List<String> command) {
// For relative paths, trust that PATH resolution will work or fail appropriately
}

/**
* Signal the on-demand unoserver manager that a conversion is needed. Writes the current epoch
* timestamp to /tmp/uno-last-used. The demand manager watches this file and:
*/
private static void signalUnoServerDemand() {
try {
Path demandFile = Path.of("/tmp/uno-last-used");
Comment thread
balazs-szucs marked this conversation as resolved.
String epoch = String.valueOf(System.currentTimeMillis() / 1000);
Files.writeString(demandFile, epoch);

waitForUnoServerReady(30);
Comment thread
balazs-szucs marked this conversation as resolved.
Outdated
} catch (IOException e) {
log.debug("Could not write unoserver demand file: {}", e.getMessage());
}
}

/**
* Wait for at least one unoserver endpoint to accept connections. Uses a simple TCP connect
* probe to the first configured endpoint.
*/
private static void waitForUnoServerReady(int maxWaitSeconds) {
Comment thread
balazs-szucs marked this conversation as resolved.
Outdated
if (unoServerPool == null || unoServerPool.isEmpty()) {
return;
}
// Try a quick TCP probe to the first endpoint
try {
var lease = unoServerPool.acquireEndpoint(1, TimeUnit.MILLISECONDS);
var endpoint = lease.getEndpoint();
lease.close();

String host = endpoint.getHost();
int port = endpoint.getPort();
if (host == null || host.isBlank()) host = "127.0.0.1";
if (port <= 0) port = 2003;

for (int i = 0; i < maxWaitSeconds; i++) {
try (var socket = new java.net.Socket()) {
socket.connect(new java.net.InetSocketAddress(host, port), 1000);
log.debug("unoserver ready on {}:{}", host, port);
return;
} catch (IOException e) {
// Not ready yet, wait
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
}
}
log.warn("unoserver not ready after {}s, proceeding anyway", maxWaitSeconds);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (TimeoutException e) {
// Pool fully occupied, unoserver is likely running
}
}

public enum Processes {
LIBRE_OFFICE,
PDFTOHTML,
Expand Down
5 changes: 5 additions & 0 deletions app/core/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ spring.devtools.restart.exclude=stirling.software.proprietary.security/**
spring.web.resources.mime-mappings.webmanifest=application/manifest+json
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
server.jetty.max-http-request-header-size=32768
server.jetty.connection-idle-timeout=PT5M

spring.datasource.url=jdbc:h2:file:./configs/stirling-pdf-DB-2.3.232;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=PostgreSQL
spring.datasource.driver-class-name=org.h2.Driver
Expand Down Expand Up @@ -98,3 +99,7 @@ java.io.tmpdir=${stirling.tempfiles.directory:${java.io.tmpdir}/stirling-pdf}

# V2 features
v2=true

# Lazy initialization to minimize startup and idle memory footprint
spring.main.lazy-initialization=true

4 changes: 1 addition & 3 deletions docker/embedded/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,7 @@ RUN echo "${VERSION_TAG:-dev}" > /etc/stirling_version
# Environment variables
ENV VERSION_TAG=$VERSION_TAG \
STIRLING_AOT_ENABLE="false" \
STIRLING_JVM_PROFILE="balanced" \
_JVM_OPTS_BALANCED="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=4m -XX:G1PeriodicGCInterval=60000 -XX:+UseStringDeduplication -XX:+UseCompactObjectHeaders -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \
_JVM_OPTS_PERFORMANCE="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UseCompactObjectHeaders -XX:+UseStringDeduplication -XX:+AlwaysPreTouch -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \
_JVM_OPTS="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UnlockExperimentalVMOptions -XX:+UseG1GC -XX:G1PeriodicGCInterval=10000 -XX:G1PeriodicGCSystemLoadThreshold=0.0 -XX:+G1PeriodicGCInvokesConcurrent -XX:+UseCompactObjectHeaders -XX:+UseStringDeduplication -XX:+ExplicitGCInvokesConcurrent -XX:ReservedCodeCacheSize=96m -Xss256k -XX:CICompilerCount=2 -Djdk.virtualThreadScheduler.maxPoolSize=4 -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \
JAVA_CUSTOM_OPTS="" \
HOME=/home/stirlingpdfuser \
PUID=1000 \
Expand Down
4 changes: 1 addition & 3 deletions docker/embedded/Dockerfile.fat
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ RUN echo "${VERSION_TAG:-dev}" > /etc/stirling_version
# Environment variables
ENV VERSION_TAG=$VERSION_TAG \
STIRLING_AOT_ENABLE="false" \
STIRLING_JVM_PROFILE="balanced" \
_JVM_OPTS_BALANCED="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=4m -XX:G1PeriodicGCInterval=60000 -XX:+UseStringDeduplication -XX:+UseCompactObjectHeaders -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \
_JVM_OPTS_PERFORMANCE="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UseCompactObjectHeaders -XX:+UseStringDeduplication -XX:+AlwaysPreTouch -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \
_JVM_OPTS="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahUncommitDelay=1000 -XX:ShenandoahGuaranteedYoungGCInterval=10000 -XX:ShenandoahGuaranteedOldGCInterval=30000 -XX:+UseCompactObjectHeaders -XX:+UseStringDeduplication -XX:+ExplicitGCInvokesConcurrent -XX:ReservedCodeCacheSize=96m -Xss256k -XX:CICompilerCount=2 -Djdk.virtualThreadScheduler.maxPoolSize=4 -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \
JAVA_CUSTOM_OPTS="" \
HOME=/home/stirlingpdfuser \
PUID=1000 \
Expand Down
4 changes: 1 addition & 3 deletions docker/embedded/Dockerfile.ultra-lite
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,7 @@ LABEL org.opencontainers.image.title="Stirling-PDF Ultra-Lite" \
# are computed dynamically by init-without-ocr.sh based on container memory limits.
ENV VERSION_TAG=$VERSION_TAG \
STIRLING_AOT_ENABLE="false" \
STIRLING_JVM_PROFILE="balanced" \
_JVM_OPTS_BALANCED="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=4m -XX:G1PeriodicGCInterval=60000 -XX:+UseStringDeduplication -XX:+UseCompactObjectHeaders -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \
_JVM_OPTS_PERFORMANCE="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:+UseCompactObjectHeaders -XX:+UseStringDeduplication -XX:+AlwaysPreTouch -XX:+ExplicitGCInvokesConcurrent -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \
_JVM_OPTS="-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/configs/heap_dumps -XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC -XX:ShenandoahGCMode=generational -XX:ShenandoahGCHeuristics=adaptive -XX:ShenandoahUncommitDelay=1000 -XX:ShenandoahGuaranteedYoungGCInterval=10000 -XX:ShenandoahGuaranteedOldGCInterval=30000 -XX:+UseCompactObjectHeaders -XX:+UseStringDeduplication -XX:+ExplicitGCInvokesConcurrent -XX:ReservedCodeCacheSize=96m -Xss256k -XX:CICompilerCount=2 -Djdk.virtualThreadScheduler.maxPoolSize=4 -Dspring.threads.virtual.enabled=true -Djava.awt.headless=true" \
JAVA_CUSTOM_OPTS="" \
HOME=/home/stirlingpdfuser \
PUID=1000 \
Expand Down
9 changes: 8 additions & 1 deletion docker/unoserver/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ ENV DEBIAN_FRONTEND=noninteractive \
UNOSERVER_INTERFACE=0.0.0.0 \
UNOSERVER_CONVERSION_TIMEOUT=1800 \
UNOSERVER_RECYCLE_INTERVAL_SECONDS=0 \
UNOSERVER_PROFILE_DIR=/var/lib/unoserver/profile
UNOSERVER_IDLE_TIMEOUT_SECONDS=0 \
UNOSERVER_PROFILE_DIR=/var/lib/unoserver/profile \
SAL_USE_VCLPLUGIN=svp \
SAL_DISABLE_PRINTERLIST=1 \
OOO_FORCE_DESKTOP=none \
SAL_LOG="-WARN-INFO" \
MALLOC_ARENA_MAX=2 \
DBUS_SESSION_BUS_ADDRESS=/dev/null

RUN set -eux; \
apt-get update; \
Expand Down
134 changes: 104 additions & 30 deletions docker/unoserver/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,23 @@ CONVERSION_TIMEOUT="${UNOSERVER_CONVERSION_TIMEOUT:-1800}"
RECYCLE_INTERVAL_SECONDS="${UNOSERVER_RECYCLE_INTERVAL_SECONDS:-0}"
RECYCLE_INTERVAL_FLOOR=60
PROFILE_DIR="${UNOSERVER_PROFILE_DIR:-/var/lib/unoserver/profile}"
IDLE_TIMEOUT="${UNOSERVER_IDLE_TIMEOUT_SECONDS:-0}"

# ---------- LibreOffice memory-reduction environment ----------
export SAL_USE_VCLPLUGIN=svp # Null rendering plugin (~40 MB savings)
export SAL_DISABLE_PRINTERLIST=1 # Skip printer enumeration
export OOO_FORCE_DESKTOP=none # No desktop frame
export SAL_LOG="-WARN-INFO" # Minimal logging
export MALLOC_ARENA_MAX=2 # Limit glibc arena fragmentation (20-80 MB savings)
export DBUS_SESSION_BUS_ADDRESS=/dev/null

log() { printf '%s %s\n' "[unoserver-entrypoint]" "$*" >&2; }

case "$PORT" in ''|*[!0-9]*) log "Invalid UNOSERVER_PORT='$PORT'"; exit 64 ;; esac
case "$UNO_PORT" in ''|*[!0-9]*) log "Invalid UNOSERVER_UNO_PORT='$UNO_PORT'"; exit 64 ;; esac
case "$CONVERSION_TIMEOUT" in ''|*[!0-9]*) log "Invalid UNOSERVER_CONVERSION_TIMEOUT='$CONVERSION_TIMEOUT'"; exit 64 ;; esac
case "$RECYCLE_INTERVAL_SECONDS" in ''|*[!0-9]*) log "Invalid UNOSERVER_RECYCLE_INTERVAL_SECONDS='$RECYCLE_INTERVAL_SECONDS'"; exit 64 ;; esac
case "$IDLE_TIMEOUT" in ''|*[!0-9]*) log "Invalid UNOSERVER_IDLE_TIMEOUT_SECONDS='$IDLE_TIMEOUT'"; exit 64 ;; esac

mkdir -p "$PROFILE_DIR"

Expand All @@ -39,10 +49,12 @@ cleanup() {
if [ -n "${XVFB_PID:-}" ] && kill -0 "$XVFB_PID" 2>/dev/null; then
kill -TERM "$XVFB_PID" 2>/dev/null || true
fi
rm -f /tmp/uno-idle-state 2>/dev/null || true
}
trap cleanup TERM INT EXIT

start_unoserver() {
rm -f /tmp/uno-idle-state 2>/dev/null || true
log "Starting unoserver on ${INTERFACE}:${PORT} (uno-port ${UNO_PORT}, timeout ${CONVERSION_TIMEOUT}s, profile ${PROFILE_DIR})"
# Pass --user-installation as a plain path; unoserver 3.6 wraps it itself
# and crashes if pre-wrapped as a file:// URI.
Expand All @@ -59,43 +71,105 @@ start_unoserver() {

# Wall-clock recycle to bound LibreOffice memory growth. wait -n is unreliable
# here because the unoserver job is wrapped in a process substitution.
# Also supports idle-shutdown when IDLE_TIMEOUT > 0.
recycle_supervisor() {
if [ "$RECYCLE_INTERVAL_SECONDS" -le 0 ]; then
log "Recycle disabled (UNOSERVER_RECYCLE_INTERVAL_SECONDS=0)"
if [ "$RECYCLE_INTERVAL_SECONDS" -le 0 ] && [ "$IDLE_TIMEOUT" -le 0 ]; then
log "Recycle disabled, idle shutdown disabled"
wait "$UNOSERVER_PID"
return $?
fi
local interval="$RECYCLE_INTERVAL_SECONDS"
if [ "$interval" -lt "$RECYCLE_INTERVAL_FLOOR" ]; then
log "Clamping recycle interval ${interval}s up to floor ${RECYCLE_INTERVAL_FLOOR}s"
interval="$RECYCLE_INTERVAL_FLOOR"

local recycle_interval=0
if [ "$RECYCLE_INTERVAL_SECONDS" -gt 0 ]; then
recycle_interval="$RECYCLE_INTERVAL_SECONDS"
if [ "$recycle_interval" -lt "$RECYCLE_INTERVAL_FLOOR" ]; then
log "Clamping recycle interval ${recycle_interval}s up to floor ${RECYCLE_INTERVAL_FLOOR}s"
recycle_interval="$RECYCLE_INTERVAL_FLOOR"
fi
log "Recycle enabled: restart every ${recycle_interval}s"
fi

if [ "$IDLE_TIMEOUT" -gt 0 ]; then
log "Idle shutdown enabled: stop after ${IDLE_TIMEOUT}s of inactivity"
fi
log "Recycle enabled: restart every ${interval}s"

# Track last activity via demand file (Java writes to this)
local demand_file="/tmp/uno-last-used"
# Mark as active at startup
date +%s > "$demand_file" 2>/dev/null || true

local elapsed=0
while true; do
local elapsed=0
while [ "$elapsed" -lt "$interval" ]; do
if ! kill -0 "$UNOSERVER_PID" 2>/dev/null; then
wait "$UNOSERVER_PID"
local rc=$?
log "unoserver exited on its own (rc=$rc); not recycling"
return "$rc"
if ! kill -0 "$UNOSERVER_PID" 2>/dev/null; then
wait "$UNOSERVER_PID"
local rc=$?
log "unoserver exited on its own (rc=$rc)"
return "$rc"
fi

sleep 1
elapsed=$((elapsed + 1))

# Idle shutdown check
if [ "$IDLE_TIMEOUT" -gt 0 ] && [ -f "$demand_file" ]; then
local last_used
last_used=$(cat "$demand_file" 2>/dev/null || echo "0")
local now
now=$(date +%s)
local idle_secs=$(( now - last_used ))
if [ "$idle_secs" -ge "$IDLE_TIMEOUT" ]; then
log "Idle for ${idle_secs}s (timeout=${IDLE_TIMEOUT}s), shutting down unoserver"
pkill -TERM -P "$UNOSERVER_PID" 2>/dev/null || true
kill -TERM "$UNOSERVER_PID" 2>/dev/null || true
for _ in 1 2 3 4 5; do
kill -0 "$UNOSERVER_PID" 2>/dev/null || break
sleep 1
done
pkill -KILL -P "$UNOSERVER_PID" 2>/dev/null || true
kill -KILL "$UNOSERVER_PID" 2>/dev/null || true
wait "$UNOSERVER_PID" 2>/dev/null || true
rm -rf "${PROFILE_DIR:?}"/* 2>/dev/null || true
touch /tmp/uno-idle-state 2>/dev/null || true
log "unoserver stopped due to idle timeout, waiting for next demand"

# Wait for demand (poll the demand file for a fresh timestamp)
while true; do
if [ -f "$demand_file" ]; then
local demand_ts
demand_ts=$(cat "$demand_file" 2>/dev/null || echo "0")
if [ "$demand_ts" -gt "$now" ] 2>/dev/null; then
log "Demand detected, restarting unoserver"
rm -f /tmp/uno-idle-state 2>/dev/null || true
start_unoserver
break
fi
fi
sleep 2
done
elapsed=0
continue
fi
sleep 1
elapsed=$((elapsed + 1))
done
log "Recycling unoserver (pid ${UNOSERVER_PID})"
pkill -TERM -P "$UNOSERVER_PID" 2>/dev/null || true
kill -TERM "$UNOSERVER_PID" 2>/dev/null || true
for _ in 1 2 3 4 5; do
kill -0 "$UNOSERVER_PID" 2>/dev/null || break
sleep 1
done
pkill -KILL -P "$UNOSERVER_PID" 2>/dev/null || true
kill -KILL "$UNOSERVER_PID" 2>/dev/null || true
wait "$UNOSERVER_PID" 2>/dev/null || true
rm -rf "${PROFILE_DIR:?}"/* 2>/dev/null || true
start_unoserver
log "unoserver restarted (pid ${UNOSERVER_PID})"
fi

# Recycle check
if [ "$recycle_interval" -gt 0 ] && [ "$elapsed" -ge "$recycle_interval" ]; then
log "Recycling unoserver (pid ${UNOSERVER_PID})"
pkill -TERM -P "$UNOSERVER_PID" 2>/dev/null || true
kill -TERM "$UNOSERVER_PID" 2>/dev/null || true
for _ in 1 2 3 4 5; do
kill -0 "$UNOSERVER_PID" 2>/dev/null || break
sleep 1
done
pkill -KILL -P "$UNOSERVER_PID" 2>/dev/null || true
kill -KILL "$UNOSERVER_PID" 2>/dev/null || true
wait "$UNOSERVER_PID" 2>/dev/null || true
rm -rf "${PROFILE_DIR:?}"/* 2>/dev/null || true
start_unoserver
log "unoserver restarted (pid ${UNOSERVER_PID})"
elapsed=0
# Mark as active after recycle
date +%s > "$demand_file" 2>/dev/null || true
fi
done
}

Expand Down
5 changes: 5 additions & 0 deletions docker/unoserver/healthcheck.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ set -eu

PORT="${UNOSERVER_PORT:-2003}"

if [ -f /tmp/uno-idle-state ]; then
echo "Idle (intentional)"
exit 0
fi

if command -v unoping >/dev/null 2>&1; then
unoping --host 127.0.0.1 --port "$PORT" >/dev/null 2>&1
exit $?
Expand Down
Loading
Loading