Add --max-old-space-size=1024 to the node process running the Next.js server. This caps V8's old-generation heap at 1024MB, forcing the garbage collector to run more aggressively as memory usage approaches the limit. Previously, users saw Next.js RSS grow to 4GB+. This was caused by a reference chain that prevented GC from freeing memory: AfterContext (vercel/next.js#89091) retained ServerResponse objects after client disconnect, which held zlib Transform streams (from compress: true), which allocated native C++ buffers outside V8's heap entirely — invisible to GC and uncapped by --max-old-space-size. Combined with the prior commit's fixes (compress: false moves gzip to nginx, httpBatchLink closes connections faster, socket-cleanup.cjs destroys zombie CLOSE_WAIT sockets every 60s), GC can now actually reclaim memory because: - zlib native buffers no longer exist (compress: false) - The AfterContext → ServerResponse reference chain is broken by socket cleanup destroying the underlying sockets - The JS heap is capped at 1024MB so V8 GCs more frequently for the memory it does control Also fixes the Dockerfile to copy socket-cleanup.cjs from the builder stage instead of the host context. Ref: https://github.com/homarr-labs/homarr/issues/3759
77 lines
2.4 KiB
Bash
77 lines
2.4 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
# Create sub directories in volume
|
|
mkdir -p /appdata/db
|
|
mkdir -p /appdata/redis
|
|
mkdir -p /appdata/trusted-certificates
|
|
|
|
# Run migrations
|
|
if [ "$DB_MIGRATIONS_DISABLED" = "true" ]; then
|
|
echo "DB migrations are disabled, skipping"
|
|
else
|
|
echo "Running DB migrations"
|
|
# We disable redis logs during migration as the redis client is not yet started
|
|
DISABLE_REDIS_LOGS=true node ./db/migrations/$DB_DIALECT/migrate.cjs ./db/migrations/$DB_DIALECT
|
|
fi
|
|
|
|
# Auth secret is generated every time the container starts as it is required, but not used because we don't need JWTs or Mail hashing
|
|
export AUTH_SECRET=$(openssl rand -base64 32)
|
|
# Cron job API key is generated every time the container starts as it is required for communication between nextjs-api and tasks-api
|
|
export CRON_JOB_API_KEY=$(openssl rand -base64 32)
|
|
|
|
# Support secret file for encryption key (is not empty and file exists)
|
|
if [ -n "$SECRET_ENCRYPTION_KEY_FILE" ] && [ -f "$SECRET_ENCRYPTION_KEY_FILE" ]; then
|
|
export SECRET_ENCRYPTION_KEY=$(cat $SECRET_ENCRYPTION_KEY_FILE)
|
|
fi
|
|
|
|
# Start nginx proxy
|
|
# 1. Replace the HOSTNAME in the nginx template file
|
|
# 2. Create the nginx configuration file from the template
|
|
# 3. Start the nginx server
|
|
export HOSTNAME
|
|
envsubst '${HOSTNAME}' < /etc/nginx/templates/nginx.conf > /etc/nginx/nginx.conf
|
|
# Start services in the background and store their PIDs
|
|
nginx -g 'daemon off;' &
|
|
NGINX_PID=$!
|
|
|
|
if [ "$REDIS_IS_EXTERNAL" = "true" ]; then
|
|
echo "Using external Redis server at redis://$REDIS_HOST:$REDIS_PORT"
|
|
REDIS_PID=""
|
|
else
|
|
echo "Starting internal Redis server"
|
|
redis-server /app/redis.conf &
|
|
REDIS_PID=$!
|
|
fi
|
|
|
|
|
|
|
|
node apps/tasks/tasks.cjs &
|
|
TASKS_PID=$!
|
|
|
|
node apps/websocket/wssServer.cjs &
|
|
WSS_PID=$!
|
|
|
|
node --max-old-space-size=1024 --require apps/nextjs/socket-cleanup.cjs apps/nextjs/server.js &
|
|
NEXTJS_PID=$!
|
|
|
|
# Function to handle SIGTERM and shut down services
|
|
terminate() {
|
|
echo "Received SIGTERM. Shutting down..."
|
|
kill -TERM $NGINX_PID $TASKS_PID $WSS_PID $NEXTJS_PID 2>/dev/null
|
|
wait
|
|
# kill redis-server last because of logging of other services and only if $REDIS_PID is set
|
|
if [ -n "$REDIS_PID" ]; then
|
|
kill -TERM $REDIS_PID 2>/dev/null
|
|
wait
|
|
fi
|
|
echo "Shutdown complete."
|
|
exit 0
|
|
}
|
|
|
|
# When SIGTERM (docker stop <container>) / SIGINT (ctrl+c) is received, run the terminate function
|
|
trap terminate TERM INT
|
|
|
|
# Wait for all processes
|
|
wait $NEXTJS_PID
|
|
terminate
|