7 Commits

Author SHA1 Message Date
Your Name
183dcf5072 Improve heap configuration controls 2025-11-07 06:25:38 -06:00
Your Name
8fc7e89cdd Strip bootstrap.password from config cleanup 2025-11-07 05:24:08 -06:00
Your Name
825d522d5c Store bootstrap password only in keystore 2025-11-07 05:19:25 -06:00
Your Name
82825fd4c4 Fix path.plugins removal 2025-11-07 05:16:09 -06:00
Your Name
eeee0301c6 Preinstall analysis-icu and drop runtime plugin install 2025-11-07 05:05:49 -06:00
Your Name
ee566734a0 Bump to 1.0.12 with Java 21 and plugin automation 2025-11-07 04:53:08 -06:00
Your Name
a2f315f959 Ensure plugin installation works on Cloudron 2025-11-07 04:17:23 -06:00
5 changed files with 194 additions and 62 deletions

View File

@@ -4,7 +4,7 @@
"author": "Elastic and Cloudron Community",
"description": "Elasticsearch is a distributed, open source search and analytics engine for all types of data. This package is designed for internal use only.",
"tagline": "Distributed search and analytics engine",
"version": "1.0.0",
"version": "1.0.17",
"healthCheckPath": "/_cluster/health?pretty",
"httpPort": 9200,
"manifestVersion": 2,
@@ -17,10 +17,6 @@
"localDir": "/data"
}
},
"accessRestriction": {
"users": true,
"defaultValue": "internal"
},
"tags": [
"elasticsearch",
"search",
@@ -37,4 +33,4 @@
"description": "Elasticsearch transport port for node-to-node communication"
}
}
}
}

View File

@@ -16,11 +16,11 @@ RUN apt-get update && \
net-tools \
iputils-ping \
dnsutils \
openjdk-17-jdk
openjdk-21-jdk
# Set Elasticsearch version
ENV ELASTIC_VERSION=9.1.5
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
# Create elasticsearch user and group
RUN groupadd elasticsearch && \
@@ -37,8 +37,12 @@ RUN mkdir -p /usr/share/elasticsearch && \
RUN mkdir -p /app/data/{elasticsearch,logs,config,secrets,jdk/bin,run} && \
chown -R elasticsearch:elasticsearch /app/data
# Preinstall required plugins
RUN ES_JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch analysis-icu
# Copy configuration files
COPY elasticsearch.yml /app/elasticsearch.yml
COPY .env.template /app/.env.template
COPY start.sh /app/start.sh
COPY stop.sh /app/stop.sh
@@ -50,4 +54,4 @@ HEALTHCHECK --interval=15s --timeout=10s --start-period=120s --retries=5 \
CMD curl -fs -u elastic:$(cat /app/data/secrets/elastic_password) http://localhost:9200/_cluster/health?pretty || exit 1
# Command to run
CMD ["/app/start.sh"]
CMD ["/app/start.sh"]

View File

@@ -32,6 +32,11 @@ After installation:
1. Check the app logs to ensure Elasticsearch has started correctly
2. Note the generated password from the logs or from `/app/data/credentials.txt`
3. Configure your other Cloudron apps to connect to Elasticsearch using the format: `http://elastic:<password>@localhost:9200`
4. (Recommended) Verify the bundled `analysis-icu` plugin (required by Nextclouds full-text search):
```bash
curl -X GET -u elastic:<password> "localhost:9200/_nodes/plugins?pretty"
```
The output should list `analysis-icu` before you kick off `occ fulltextsearch:index`. If you need additional plugins, rebuild this package with the desired plugin baked into the image (runtime installation is blocked by Elasticsearch 9s filesystem restrictions).
## Troubleshooting
@@ -174,4 +179,4 @@ If successful, you should see a JSON response with Elasticsearch information.
#### Advanced Configuration
In some cases, you might need to modify additional Elasticsearch settings. You can do this via the elasticsearch.yml file, which is stored in `/app/data/config/elasticsearch.yml` within the Elasticsearch app container.
In some cases, you might need to modify additional Elasticsearch settings. You can do this via the elasticsearch.yml file, which is stored in `/app/data/config/elasticsearch.yml` within the Elasticsearch app container.

View File

@@ -7,6 +7,7 @@ This package provides Elasticsearch for Cloudron, configured for internal use on
- Elasticsearch 9.1.5
- Single-node configuration optimized for Cloudron
- Security enabled with basic authentication
- Automatically installs the `analysis-icu` plugin (configurable)
- Internal access only by default (not publicly exposed)
- Automatic optimization based on container resources
@@ -69,6 +70,16 @@ curl -X PUT "http://elastic:PASSWORD@IPADDRESS:9200/nextcloud" -H 'Content-Type:
You can get the IP address from the Cloudron admin panel or by using the `cloudron status` command. The password is stored in `/app/data/credentials.txt`.
### Language Analysis Plugins
Many integrations (for example, Nextcloud Full-Text Search with German documents) require the `analysis-icu` plugin so Elasticsearch understands language-specific analyzers. This package ships with `analysis-icu` preinstalled. You can verify it from the Elasticsearch web terminal:
```bash
curl -X GET -u elastic:<password> "localhost:9200/_nodes/plugins?pretty"
```
Look for `analysis-icu` in the output before running `occ fulltextsearch:index`. If you need additional plugins, rebuild this package with the desired plugin installed in the Docker image (Elasticsearch 9 no longer allows runtime plugin installation into read-only filesystems).
## Security Notes
- The app is configured as internal-only by default, so it's not exposed to the public internet
@@ -82,10 +93,17 @@ You can get the IP address from the Cloudron admin panel or by using the `cloudr
The package automatically configures Elasticsearch based on the container's available resources:
- Java heap size is set to 50% of available memory
- Java heap size defaults to 50% of the app's memory allocation
- GC optimization for container environments
- Index settings tuned for single-node operation
You can override the automatic heap sizing when necessary:
- Define `ES_JAVA_HEAP` (e.g. `export ES_JAVA_HEAP=8g`) in `/app/data/.env`
- Provide a full `ES_JAVA_OPTS` string in `/app/data/.env` to take complete control
- Add explicit `-Xms`/`-Xmx` entries in `/app/data/config/jvm.options.d/heap.options`
- Set `ES_SKIP_AUTO_HEAP=1` in `/app/data/.env` to rely solely on the configuration files
## Limitations
- This package is for internal use only and is not exposed to the web by default
@@ -94,4 +112,4 @@ The package automatically configures Elasticsearch based on the container's avai
## Support
For support, please create an issue on the package's GitHub repository or contact the package maintainer.
For support, please create an issue on the package's GitHub repository or contact the package maintainer.

209
start.sh
View File

@@ -56,21 +56,34 @@ setup_password() {
# Set up Java environment
setup_java() {
if [ -L /app/data/jdk/bin/java ]; then
mkdir -p /app/data/jdk/bin
if [ -x /app/data/jdk/bin/java ]; then
echo "Java already configured: $(/app/data/jdk/bin/java -version 2>&1 | head -n 1)"
return 0
fi
echo "Setting up Java environment..."
# Try Java 17 first, then fall back to system Java
if [ -f /usr/lib/jvm/java-17-openjdk-amd64/bin/java ]; then
ln -sf /usr/lib/jvm/java-17-openjdk-amd64/bin/{java,javac,javadoc,jar} /app/data/jdk/bin/
else
JAVA_PATH=$(which java)
if [ -n "$JAVA_PATH" ]; then
ln -sf $JAVA_PATH /app/data/jdk/bin/java
JAVA_FOUND=0
for candidate in /usr/lib/jvm/java-21-openjdk-amd64/bin /usr/lib/jvm/java-17-openjdk-amd64/bin; do
if [ -x "$candidate/java" ]; then
ln -sf "$candidate/java" /app/data/jdk/bin/java
for tool in javac javadoc jar; do
ln -sf $(which $tool 2>/dev/null) /app/data/jdk/bin/$tool 2>/dev/null || true
[ -x "$candidate/$tool" ] && ln -sf "$candidate/$tool" /app/data/jdk/bin/$tool
done
JAVA_FOUND=1
break
fi
done
if [ $JAVA_FOUND -eq 0 ]; then
JAVA_PATH=$(command -v java || true)
if [ -n "$JAVA_PATH" ]; then
ln -sf "$JAVA_PATH" /app/data/jdk/bin/java
for tool in javac javadoc jar; do
TOOL_PATH=$(command -v $tool 2>/dev/null || true)
[ -n "$TOOL_PATH" ] && ln -sf "$TOOL_PATH" /app/data/jdk/bin/$tool
done
else
echo "ERROR: No Java found on system. Elasticsearch requires Java to run."
@@ -78,15 +91,13 @@ setup_java() {
fi
fi
# Verify Java is available
if [ -L /app/data/jdk/bin/java ]; then
echo "Java version: $(/app/data/jdk/bin/java -version 2>&1 | head -n 1)"
else
if [ ! -x /app/data/jdk/bin/java ]; then
echo "ERROR: Failed to link Java executable"
ls -l /app/data/jdk/bin || true
exit 1
fi
# Ensure Java symlinks have correct ownership
echo "Java version: $(/app/data/jdk/bin/java -version 2>&1 | head -n 1)"
chown -R elasticsearch:elasticsearch /app/data/jdk
}
@@ -100,24 +111,6 @@ configure_elasticsearch() {
chmod 755 $ES_PATH_CONF
chown -R elasticsearch:elasticsearch $ES_PATH_CONF
# Add bootstrap password to keystore
echo "Setting bootstrap password..."
# Verify keystore exists before trying to add password
if [ ! -f $ES_PATH_CONF/elasticsearch.keystore ]; then
echo "ERROR: Keystore not found, cannot add bootstrap password. Creating keystore first..."
setup_keystore || {
echo "CRITICAL ERROR: Failed to create keystore, cannot proceed."
exit 1
}
fi
# Now add the bootstrap password
if ! printf "%s" "$ELASTIC_PASSWORD" | su -c "ES_PATH_CONF=$ES_PATH_CONF ES_JAVA_HOME=/app/data/jdk $ES_HOME/bin/elasticsearch-keystore add -f -x 'bootstrap.password' --stdin" elasticsearch; then
echo "ERROR: Failed to add bootstrap password to keystore."
exit 1
fi
# Copy configuration files if needed
if [ ! -f $ES_PATH_CONF/elasticsearch.yml ]; then
echo "Setting up configuration files..."
@@ -126,6 +119,14 @@ configure_elasticsearch() {
chown -R elasticsearch:elasticsearch $ES_PATH_CONF
fi
ensure_directory_structure
clean_legacy_settings
ensure_setting "xpack.security.http.ssl.enabled" "false"
ensure_setting "network.host" "0.0.0.0"
ensure_setting "discovery.type" "single-node"
ensure_setting "path.data" "/app/data/elasticsearch"
ensure_setting "path.logs" "/app/data/logs"
# CRITICAL FIX: Remove any index-level settings from elasticsearch.yml to prevent startup failure
if [ -f $ES_PATH_CONF/elasticsearch.yml ]; then
echo "Checking elasticsearch.yml for index-level settings..."
@@ -204,13 +205,6 @@ configure_elasticsearch() {
chmod 600 $ES_PATH_CONF/{users,users_roles}
fi
# Ensure basic settings in elasticsearch.yml
for setting in "xpack.security.http.ssl.enabled: false" "network.host: 0.0.0.0" "discovery.type: single-node"; do
if ! grep -q "$setting" $ES_PATH_CONF/elasticsearch.yml; then
echo "$setting" >> $ES_PATH_CONF/elasticsearch.yml
fi
done
# Final permission check
echo "Final permission check on all data directories..."
chown -R elasticsearch:elasticsearch /app/data
@@ -360,20 +354,135 @@ setup_keystore() {
return 0
}
# Ensure writable dirs exist
ensure_directory_structure() {
mkdir -p /app/data/elasticsearch
mkdir -p /app/data/elasticsearch/plugins
mkdir -p /app/data/logs
chown -R elasticsearch:elasticsearch /app/data/elasticsearch /app/data/logs
}
clean_legacy_settings() {
sed -i '/^path\.home:/d' "$ES_PATH_CONF/elasticsearch.yml"
sed -i '/^path\.data:/d' "$ES_PATH_CONF/elasticsearch.yml"
sed -i '/^path\.logs:/d' "$ES_PATH_CONF/elasticsearch.yml"
sed -i '/^path\.plugins:/d' "$ES_PATH_CONF/elasticsearch.yml"
sed -i '/^bootstrap\.password:/d' "$ES_PATH_CONF/elasticsearch.yml"
}
ensure_setting() {
local key="$1"
local value="$2"
if grep -q "^$key:" "$ES_PATH_CONF/elasticsearch.yml"; then
sed -i "s|^$key:.*|$key: $value|" "$ES_PATH_CONF/elasticsearch.yml"
else
echo "$key: $value" >> "$ES_PATH_CONF/elasticsearch.yml"
fi
}
default_gc_opts() {
echo "-XX:+UseG1GC -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -Xlog:gc*,gc+age=trace,safepoint:file=/app/data/logs/gc/gc.log:utctime,level,pid,tags:filecount=32,filesize=64m"
}
normalize_heap_value() {
local raw="${1:-}"
raw=$(echo "$raw" | tr '[:upper:]' '[:lower:]')
if [[ "$raw" =~ ^[0-9]+[mg]$ ]]; then
echo "$raw"
return 0
fi
if [[ "$raw" =~ ^[0-9]+$ ]]; then
echo "${raw}m"
return 0
fi
return 1
}
detect_memory_limit_bytes() {
local limit=""
if [ -r /sys/fs/cgroup/memory.max ]; then
limit=$(cat /sys/fs/cgroup/memory.max)
if [ "$limit" != "max" ]; then
echo "$limit"
return 0
fi
fi
if [ -r /sys/fs/cgroup/memory/memory.limit_in_bytes ]; then
limit=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes)
echo "$limit"
return 0
fi
if [ -n "${CLOUDRON_MEMORY_LIMIT:-}" ]; then
# CLOUDRON_MEMORY_LIMIT is in MB
echo $((CLOUDRON_MEMORY_LIMIT * 1024 * 1024))
return 0
fi
echo ""
}
has_custom_heap_file() {
local heap_file="$ES_PATH_CONF/jvm.options.d/heap.options"
if [ -f "$heap_file" ] && grep -Eq '^-Xms|^-Xmx' "$heap_file"; then
return 0
fi
return 1
}
# Configure JVM heap size
configure_heap() {
# Calculate optimal heap size (50% of available memory)
CONTAINER_MEM=$(cat /sys/fs/cgroup/memory.max 2>/dev/null || echo "4294967296")
[ "$CONTAINER_MEM" = "max" ] && CONTAINER_MEM="4294967296" # Default to 4GB if unlimited
if [ "${ES_SKIP_AUTO_HEAP:-0}" = "1" ]; then
echo "Skipping automatic heap sizing because ES_SKIP_AUTO_HEAP=1"
return 0
fi
HEAP_SIZE=$(expr $CONTAINER_MEM / 2097152) # Convert to MB and take 50%
[ $HEAP_SIZE -gt 31744 ] && HEAP_SIZE=31744 # Max 31GB
[ $HEAP_SIZE -lt 512 ] && HEAP_SIZE=512 # Min 512MB
if [ -n "${ES_JAVA_OPTS:-}" ]; then
echo "Detected user-specified ES_JAVA_OPTS; not overriding heap settings."
return 0
fi
# Set JVM options
export ES_JAVA_OPTS="-Xms${HEAP_SIZE}m -Xmx${HEAP_SIZE}m -XX:+UseG1GC -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30"
export ES_JAVA_OPTS="$ES_JAVA_OPTS -Xlog:gc*,gc+age=trace,safepoint:file=/app/data/logs/gc/gc.log:utctime,level,pid,tags:filecount=32,filesize=64m"
if has_custom_heap_file; then
echo "Detected custom heap options in jvm.options.d; not overriding heap settings."
return 0
fi
local heap_value=""
if [ -n "${ES_JAVA_HEAP:-}" ]; then
if heap_value=$(normalize_heap_value "$ES_JAVA_HEAP"); then
echo "Using heap size from ES_JAVA_HEAP (${heap_value})."
else
echo "Warning: ES_JAVA_HEAP value '$ES_JAVA_HEAP' is invalid; falling back to automatic sizing."
heap_value=""
fi
fi
if [ -z "$heap_value" ]; then
local limit_bytes
limit_bytes=$(detect_memory_limit_bytes)
if [ -z "$limit_bytes" ] || [ "$limit_bytes" = "0" ]; then
limit_bytes=$((4 * 1024 * 1024 * 1024))
fi
# Treat huge values as "unlimited" and fall back to 4GB to avoid 31GB default
if [ "$limit_bytes" -gt $((256 * 1024 * 1024 * 1024)) ]; then
limit_bytes=$((4 * 1024 * 1024 * 1024))
fi
local heap_mb=$((limit_bytes / 1024 / 1024 / 2))
[ "$heap_mb" -gt 31744 ] && heap_mb=31744
[ "$heap_mb" -lt 512 ] && heap_mb=512
heap_value="${heap_mb}m"
echo "Auto-configured heap size to ${heap_value} based on container limit."
fi
export ES_JAVA_OPTS="-Xms${heap_value} -Xmx${heap_value} $(default_gc_opts)"
export PATH=$ES_HOME/bin:$PATH
}
@@ -385,7 +494,7 @@ start_elasticsearch() {
# Command to start Elasticsearch
ES_START_CMD="ES_PATH_CONF=$ES_PATH_CONF ES_JAVA_HOME=/app/data/jdk $ES_HOME/bin/elasticsearch"
ES_START_CMD="$ES_START_CMD -E xpack.security.enabled=true -E bootstrap.password=$ELASTIC_PASSWORD"
ES_START_CMD="$ES_START_CMD -E xpack.security.enabled=true"
# Add explicit settings for transport SSL
ES_START_CMD="$ES_START_CMD -E xpack.security.transport.ssl.enabled=true"
ES_START_CMD="$ES_START_CMD -E xpack.security.transport.ssl.verification_mode=certificate"
@@ -464,4 +573,4 @@ configure_heap
start_elasticsearch
# Keep container running
tail -f /app/data/logs/*.log 2>/dev/null || sleep infinity
tail -f /app/data/logs/*.log 2>/dev/null || sleep infinity