You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ho...@apache.org on 2022/08/04 19:26:33 UTC

[solr-docker] branch main updated: Manage all supported Solr images (8.11+) (#8)

This is an automated email from the ASF dual-hosted git repository.

houston pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-docker.git


The following commit(s) were added to refs/heads/main by this push:
     new 3a61966  Manage all supported Solr images (8.11+) (#8)
3a61966 is described below

commit 3a61966de4da0ef7a3e9f2d29959d6bcb2ea117e
Author: Houston Putman <ho...@apache.org>
AuthorDate: Thu Aug 4 15:26:28 2022 -0400

    Manage all supported Solr images (8.11+) (#8)
    
    This will remove the need for the old repo: https://github.com/docker-solr/docker-solr
---
 8.11-slim/Dockerfile                    | 124 ++++++++++++++++++++++++
 8.11-slim/scripts/docker-entrypoint.sh  |  31 ++++++
 8.11-slim/scripts/init-var-solr         |  61 ++++++++++++
 8.11-slim/scripts/oom_solr.sh           |  38 ++++++++
 8.11-slim/scripts/precreate-core        |  40 ++++++++
 8.11-slim/scripts/run-initdb            |  28 ++++++
 8.11-slim/scripts/solr-create           |  66 +++++++++++++
 8.11-slim/scripts/solr-demo             |  42 ++++++++
 8.11-slim/scripts/solr-fg               |  57 +++++++++++
 8.11-slim/scripts/solr-foreground       |  15 +++
 8.11-slim/scripts/solr-precreate        |  27 ++++++
 8.11-slim/scripts/start-local-solr      |  21 ++++
 8.11-slim/scripts/stop-local-solr       |  11 +++
 8.11-slim/scripts/wait-for-solr.sh      |  98 +++++++++++++++++++
 8.11-slim/scripts/wait-for-zookeeper.sh | 165 ++++++++++++++++++++++++++++++++
 8.11/Dockerfile                         | 124 ++++++++++++++++++++++++
 8.11/scripts/docker-entrypoint.sh       |  31 ++++++
 8.11/scripts/init-var-solr              |  61 ++++++++++++
 8.11/scripts/oom_solr.sh                |  38 ++++++++
 8.11/scripts/precreate-core             |  40 ++++++++
 8.11/scripts/run-initdb                 |  28 ++++++
 8.11/scripts/solr-create                |  66 +++++++++++++
 8.11/scripts/solr-demo                  |  42 ++++++++
 8.11/scripts/solr-fg                    |  57 +++++++++++
 8.11/scripts/solr-foreground            |  15 +++
 8.11/scripts/solr-precreate             |  27 ++++++
 8.11/scripts/start-local-solr           |  21 ++++
 8.11/scripts/stop-local-solr            |  11 +++
 8.11/scripts/wait-for-solr.sh           |  98 +++++++++++++++++++
 8.11/scripts/wait-for-zookeeper.sh      | 165 ++++++++++++++++++++++++++++++++
 generate-stackbrew-library.sh           |   7 +-
 31 files changed, 1651 insertions(+), 4 deletions(-)

diff --git a/8.11-slim/Dockerfile b/8.11-slim/Dockerfile
new file mode 100644
index 0000000..76f913d
--- /dev/null
+++ b/8.11-slim/Dockerfile
@@ -0,0 +1,124 @@
+
+FROM openjdk:11-jre-slim
+
+LABEL maintainer="The Apache Lucene/Solr Project"
+LABEL repository="https://github.com/docker-solr/docker-solr"
+
+ARG SOLR_VERSION="8.11.2"
+ARG SOLR_SHA512="22fedcc0090eda72c3c5a5ea769c93adaf7d92c5c4479993f009ef0b9d42de5bd2ed1e0565ca01f3428587d8a3836286aa3017aab157050f2bd5bc3482fdebaa"
+ARG SOLR_KEYS="86EDB9C33B8517228E88A8F93E48C0C6EF362B9E"
+# If specified, this will override SOLR_DOWNLOAD_SERVER and all ASF mirrors. Typically used downstream for custom builds
+ARG SOLR_DOWNLOAD_URL
+
+# Override the solr download location with e.g.:
+#   docker build -t mine --build-arg SOLR_DOWNLOAD_SERVER=http://www-eu.apache.org/dist/lucene/solr .
+ARG SOLR_DOWNLOAD_SERVER
+
+RUN set -ex; \
+  apt-get update; \
+  apt-get -y install acl dirmngr gpg lsof procps wget netcat gosu tini; \
+  rm -rf /var/lib/apt/lists/*; \
+  cd /usr/local/bin; wget -nv https://github.com/apangin/jattach/releases/download/v1.5/jattach; chmod 755 jattach; \
+  echo >jattach.sha512 "d8eedbb3e192a8596c08efedff99b9acf1075331e1747107c07cdb1718db2abe259ef168109e46bd4cf80d47d43028ff469f95e6ddcbdda4d7ffa73a20e852f9  jattach"; \
+  sha512sum -c jattach.sha512; rm jattach.sha512
+
+ENV SOLR_USER="solr" \
+    SOLR_UID="8983" \
+    SOLR_GROUP="solr" \
+    SOLR_GID="8983" \
+    SOLR_CLOSER_URL="http://www.apache.org/dyn/closer.lua?filename=lucene/solr/$SOLR_VERSION/solr-$SOLR_VERSION.tgz&action=download" \
+    SOLR_DIST_URL="https://www.apache.org/dist/lucene/solr/$SOLR_VERSION/solr-$SOLR_VERSION.tgz" \
+    SOLR_ARCHIVE_URL="https://archive.apache.org/dist/lucene/solr/$SOLR_VERSION/solr-$SOLR_VERSION.tgz" \
+    PATH="/opt/solr/bin:/opt/docker-solr/scripts:$PATH" \
+    SOLR_INCLUDE=/etc/default/solr.in.sh \
+    SOLR_HOME=/var/solr/data \
+    SOLR_PID_DIR=/var/solr \
+    SOLR_LOGS_DIR=/var/solr/logs \
+    LOG4J_PROPS=/var/solr/log4j2.xml
+
+RUN set -ex; \
+  groupadd -r --gid "$SOLR_GID" "$SOLR_GROUP"; \
+  useradd -r --uid "$SOLR_UID" --gid "$SOLR_GID" "$SOLR_USER"
+
+RUN set -ex; \
+  export GNUPGHOME="/tmp/gnupg_home"; \
+  mkdir -p "$GNUPGHOME"; \
+  chmod 700 "$GNUPGHOME"; \
+  echo "disable-ipv6" >> "$GNUPGHOME/dirmngr.conf"; \
+  for key in $SOLR_KEYS; do \
+    found=''; \
+    for server in \
+      ha.pool.sks-keyservers.net \
+      hkp://keyserver.ubuntu.com:80 \
+      hkp://p80.pool.sks-keyservers.net:80 \
+      pgp.mit.edu \
+    ; do \
+      echo "  trying $server for $key"; \
+      gpg --batch --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$key" && found=yes && break; \
+      gpg --batch --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$key" && found=yes && break; \
+    done; \
+    test -z "$found" && echo >&2 "error: failed to fetch $key from several disparate servers -- network issues?" && exit 1; \
+  done; \
+  exit 0
+
+RUN set -ex; \
+  export GNUPGHOME="/tmp/gnupg_home"; \
+  MAX_REDIRECTS=1; \
+  if [ -n "$SOLR_DOWNLOAD_URL" ]; then \
+    # If a custom URL is defined, we download from non-ASF mirror URL and allow more redirects and skip GPG step
+    # This takes effect only if the SOLR_DOWNLOAD_URL build-arg is specified, typically in downstream Dockerfiles
+    MAX_REDIRECTS=4; \
+    SKIP_GPG_CHECK=true; \
+  elif [ -n "$SOLR_DOWNLOAD_SERVER" ]; then \
+    SOLR_DOWNLOAD_URL="$SOLR_DOWNLOAD_SERVER/$SOLR_VERSION/solr-$SOLR_VERSION.tgz"; \
+  fi; \
+  for url in $SOLR_DOWNLOAD_URL $SOLR_CLOSER_URL $SOLR_DIST_URL $SOLR_ARCHIVE_URL; do \
+    if [ -f "/opt/solr-$SOLR_VERSION.tgz" ]; then break; fi; \
+    echo "downloading $url"; \
+    if wget -t 10 --max-redirect $MAX_REDIRECTS --retry-connrefused -nv "$url" -O "/opt/solr-$SOLR_VERSION.tgz"; then break; else rm -f "/opt/solr-$SOLR_VERSION.tgz"; fi; \
+  done; \
+  if [ ! -f "/opt/solr-$SOLR_VERSION.tgz" ]; then echo "failed all download attempts for solr-$SOLR_VERSION.tgz"; exit 1; fi; \
+  if [ -z "$SKIP_GPG_CHECK" ]; then \
+    echo "downloading $SOLR_ARCHIVE_URL.asc"; \
+    wget -nv "$SOLR_ARCHIVE_URL.asc" -O "/opt/solr-$SOLR_VERSION.tgz.asc"; \
+    echo "$SOLR_SHA512 */opt/solr-$SOLR_VERSION.tgz" | sha512sum -c -; \
+    (>&2 ls -l "/opt/solr-$SOLR_VERSION.tgz" "/opt/solr-$SOLR_VERSION.tgz.asc"); \
+    gpg --batch --verify "/opt/solr-$SOLR_VERSION.tgz.asc" "/opt/solr-$SOLR_VERSION.tgz"; \
+  else \
+    echo "Skipping GPG validation due to non-Apache build"; \
+  fi; \
+  tar -C /opt --extract --file "/opt/solr-$SOLR_VERSION.tgz"; \
+  (cd /opt; ln -s "solr-$SOLR_VERSION" solr); \
+  rm "/opt/solr-$SOLR_VERSION.tgz"*; \
+  rm -Rf /opt/solr/docs/ /opt/solr/dist/{solr-core-$SOLR_VERSION.jar,solr-solrj-$SOLR_VERSION.jar,solrj-lib,solr-test-framework-$SOLR_VERSION.jar,test-framework}; \
+  mkdir -p /opt/solr/server/solr/lib /docker-entrypoint-initdb.d /opt/docker-solr; \
+  chown -R 0:0 "/opt/solr-$SOLR_VERSION"; \
+  find "/opt/solr-$SOLR_VERSION" -type d -print0 | xargs -0 chmod 0755; \
+  find "/opt/solr-$SOLR_VERSION" -type f -print0 | xargs -0 chmod 0644; \
+  chmod -R 0755 "/opt/solr-$SOLR_VERSION/bin" "/opt/solr-$SOLR_VERSION/contrib/prometheus-exporter/bin/solr-exporter" /opt/solr-$SOLR_VERSION/server/scripts/cloud-scripts; \
+  cp /opt/solr/bin/solr.in.sh /etc/default/solr.in.sh; \
+  mv /opt/solr/bin/solr.in.sh /opt/solr/bin/solr.in.sh.orig; \
+  mv /opt/solr/bin/solr.in.cmd /opt/solr/bin/solr.in.cmd.orig; \
+  chown root:0 /etc/default/solr.in.sh; \
+  chmod 0664 /etc/default/solr.in.sh; \
+  mkdir -p /var/solr/data /var/solr/logs; \
+  (cd /opt/solr/server/solr; cp solr.xml zoo.cfg /var/solr/data/); \
+  cp /opt/solr/server/resources/log4j2.xml /var/solr/log4j2.xml; \
+  find /var/solr -type d -print0 | xargs -0 chmod 0770; \
+  find /var/solr -type f -print0 | xargs -0 chmod 0660; \
+  sed -i -e "s/\"\$(whoami)\" == \"root\"/\$(id -u) == 0/" /opt/solr/bin/solr; \
+  sed -i -e 's/lsof -PniTCP:/lsof -t -PniTCP:/' /opt/solr/bin/solr; \
+  chown -R "0:0" /opt/solr-$SOLR_VERSION /docker-entrypoint-initdb.d /opt/docker-solr; \
+  chown -R "$SOLR_USER:0" /var/solr; \
+  { command -v gpgconf; gpgconf --kill all || :; }; \
+  rm -r "$GNUPGHOME"
+
+COPY --chown=0:0 scripts /opt/docker-solr/scripts
+
+VOLUME /var/solr
+EXPOSE 8983
+WORKDIR /opt/solr
+USER $SOLR_USER
+
+ENTRYPOINT ["docker-entrypoint.sh"]
+CMD ["solr-foreground"]
diff --git a/8.11-slim/scripts/docker-entrypoint.sh b/8.11-slim/scripts/docker-entrypoint.sh
new file mode 100755
index 0000000..0610049
--- /dev/null
+++ b/8.11-slim/scripts/docker-entrypoint.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# docker-entrypoint for docker-solr
+
+set -e
+
+# Clear some variables that we don't want runtime
+unset SOLR_USER SOLR_UID SOLR_GROUP SOLR_GID \
+      SOLR_CLOSER_URL SOLR_DIST_URL SOLR_ARCHIVE_URL SOLR_DOWNLOAD_URL SOLR_DOWNLOAD_SERVER SOLR_KEYS SOLR_SHA512
+
+if [[ "$VERBOSE" == "yes" ]]; then
+    set -x
+fi
+
+if [[ -v SOLR_PORT ]] && ! grep -E -q '^[0-9]+$' <<<"${SOLR_PORT:-}"; then
+  SOLR_PORT=8983
+  export SOLR_PORT
+fi
+
+# when invoked with e.g.: docker run solr -help
+if [ "${1:0:1}" == '-' ]; then
+    set -- solr-foreground "$@"
+fi
+
+# execute command passed in as arguments.
+# The Dockerfile has specified the PATH to include
+# /opt/solr/bin (for Solr) and /opt/docker-solr/scripts (for our scripts
+# like solr-foreground, solr-create, solr-precreate, solr-demo).
+# Note: if you specify "solr", you'll typically want to add -f to run it in
+# the foreground.
+exec "$@"
diff --git a/8.11-slim/scripts/init-var-solr b/8.11-slim/scripts/init-var-solr
new file mode 100755
index 0000000..8a29de1
--- /dev/null
+++ b/8.11-slim/scripts/init-var-solr
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# A helper script to initialise an empty $DIR
+# If you use volumes then Docker will copy the $DIR content from the container to the volume.
+# If you use bind mounts, that does not happen, so we do it here.
+
+set -e
+
+if [[ "$VERBOSE" == "yes" ]]; then
+    set -x
+fi
+
+if [[ -n "${NO_INIT_VAR_SOLR:-}" ]]; then
+    exit 0
+fi
+
+DIR=${1:-/var/solr}
+
+if [ ! -d "$DIR" ]; then
+    echo "Missing $DIR"
+    exit 1
+fi
+
+function check_dir_writability {
+    local dir="$1"
+    if [ ! -w "$dir" ]; then
+        echo "Cannot write to $dir as $(id -u):$(id -g)"
+        ls -ld "$dir"
+        exit 1
+    fi
+}
+
+if [ ! -d "$DIR/data" ]; then
+    echo "Creating $DIR/data"
+    check_dir_writability "$DIR"
+    mkdir "$DIR/data"
+    chmod 0770 "$DIR/data"
+fi
+
+if [ ! -d "$DIR/logs" ]; then
+    echo "Creating $DIR/logs"
+    check_dir_writability "$DIR"
+    mkdir "$DIR/logs"
+    chmod 0770 "$DIR/logs"
+fi
+
+if [ ! -f "$DIR/data/solr.xml" ]; then
+    echo "Copying solr.xml"
+    cp -a /opt/solr/server/solr/solr.xml "$DIR/data/solr.xml"
+fi
+
+if [ ! -f "$DIR/data/zoo.cfg" ]; then
+    echo "Copying zoo.cfg"
+    cp -a /opt/solr/server/solr/zoo.cfg "$DIR/data/zoo.cfg"
+fi
+
+if [ ! -f "$DIR/log4j2.xml" ]; then
+    echo "Copying log4j2.xml"
+    cp -a /opt/solr/server/resources/log4j2.xml "$DIR/log4j2.xml"
+fi
+
diff --git a/8.11-slim/scripts/oom_solr.sh b/8.11-slim/scripts/oom_solr.sh
new file mode 100755
index 0000000..bed5095
--- /dev/null
+++ b/8.11-slim/scripts/oom_solr.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+# Custom oom handler loosely based on
+# https://github.com/apache/lucene-solr/blob/master/solr/bin/oom_solr.sh
+# See solr-forgeground for how to configure OOM behaviour
+
+if [[ -z "${SOLR_LOGS_DIR:-}" ]]; then
+    if [ -d /var/solr/logs ]; then
+        SOLR_LOGS_DIR=/var/solr/logs
+    elif [ -d /opt/solr/server/logs ]; then
+        SOLR_LOGS_DIR=/opt/solr/server/logs
+    else
+        echo "Cannot determine SOLR_LOGS_DIR!"
+        exit 1
+    fi
+fi
+SOLR_PID=$(pgrep -f start.jar)
+if [[ -z "$SOLR_PID" ]]; then
+  echo "Couldn't find Solr process running!"
+  exit
+fi
+
+NOW=$(date +"%F_%H_%M_%S")
+(
+echo "Running OOM killer script for Solr process $SOLR_PID"
+if [[ "$SOLR_PID" == 1 ]]; then
+  # under Docker, when running as pid 1, a SIGKILL is ignored,
+  # so use the default SIGTERM
+  kill "$SOLR_PID"
+  sleep 2
+  # if that hasn't worked, send SIGKILL
+  kill -SIGILL "$SOLR_PID"
+else
+  # if we're running with `--init` or under tini or similar,
+  # follow the upstream behaviour
+  kill -9 "$SOLR_PID"
+fi
+) | tee "$SOLR_LOGS_DIR/solr_oom_killer-$SOLR_PORT-$NOW.log"
diff --git a/8.11-slim/scripts/precreate-core b/8.11-slim/scripts/precreate-core
new file mode 100755
index 0000000..bca0f48
--- /dev/null
+++ b/8.11-slim/scripts/precreate-core
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Create a core on disk
+# arguments are: corename configdir
+
+set -e
+
+echo "Executing $0" "$@"
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+CORE=${1:-gettingstarted}
+CONFIG_SOURCE="${2:-}"
+if [[ -z "$CONFIG_SOURCE" ]]; then
+    DEFAULT_CONFIGS=(_default data_driven_schema_configs)
+    for config_dir in "${DEFAULT_CONFIGS[@]}"; do
+        config_dir="/opt/solr/server/solr/configsets/$config_dir"
+        if [ -d "$config_dir" ]; then
+           CONFIG_SOURCE="$config_dir"
+           break
+        fi
+    done
+    if [[ -z $CONFIG_SOURCE ]]; then
+        echo "Cannot find default config"
+        exit 1
+    fi
+fi
+
+coresdir=/var/solr/data
+
+coredir="$coresdir/$CORE"
+if [[ ! -d $coredir ]]; then
+    cp -r "$CONFIG_SOURCE/" "$coredir"
+    touch "$coredir/core.properties"
+    echo "Created $CORE"
+else
+    echo "Core $CORE already exists"
+fi
diff --git a/8.11-slim/scripts/run-initdb b/8.11-slim/scripts/run-initdb
new file mode 100755
index 0000000..5f250c9
--- /dev/null
+++ b/8.11-slim/scripts/run-initdb
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Run the init-solr-home script and source any '.sh' scripts in
+# /docker-entrypoint-initdb.d.
+# This script is sourced by some of the solr-* commands, so that
+# you can run eg:
+#
+#   mkdir initdb; echo "echo hi" > initdb/hi.sh
+#   docker run -v $PWD/initdb:/docker-entrypoint-initdb.d solr
+#
+# and have your script execute before Solr starts.
+#
+# Note: scripts can modify the environment, which will affect
+# subsequent scripts and ultimately Solr. That allows you to set
+# environment variables from your scripts (though you usually just
+# use "docker run -e"). If this is undesirable in your use-case,
+# have your scripts execute a sub-shell.
+
+set -e
+
+# execute files in /docker-entrypoint-initdb.d before starting solr
+while read -r f; do
+    case "$f" in
+        *.sh)     echo "$0: running $f"; . "$f" ;;
+        *)        echo "$0: ignoring $f" ;;
+    esac
+    echo
+done < <(find /docker-entrypoint-initdb.d/ -mindepth 1 -type f | sort -n)
diff --git a/8.11-slim/scripts/solr-create b/8.11-slim/scripts/solr-create
new file mode 100755
index 0000000..e2054a1
--- /dev/null
+++ b/8.11-slim/scripts/solr-create
@@ -0,0 +1,66 @@
+#!/bin/bash
+#
+# This script starts Solr on localhost, creates a core with "solr create",
+# stops Solr, and then starts Solr as normal.
+# Any arguments are passed to the "solr create".
+# To simply create a core:
+#      docker run -P -d solr solr-create -c mycore
+# To create a core from mounted config:
+#      docker run -P -d -v $PWD/myconfig:/myconfig solr solr-create -c mycore -d /myconfig
+
+set -euo pipefail
+echo "Executing $0" "$@"
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+# init script for handling an empty /var/solr
+/opt/docker-solr/scripts/init-var-solr
+
+. /opt/docker-solr/scripts/run-initdb
+
+# solr uses "-c corename". Parse the arguments to determine the core name.
+CORE_NAME="$(
+  while (( $# > 0 )); do
+    if [[ "$1" == '-c' ]]; then
+      shift
+      echo "$1"
+    fi
+    shift
+  done
+)"
+if [[ -z "${CORE_NAME:-}" ]]; then
+  echo "Could not determine core name"
+  exit 1
+fi
+
+coresdir=/var/solr/data
+CORE_DIR="$coresdir/$CORE_NAME"
+
+if [[ -d $CORE_DIR ]]; then
+    echo "Directory $CORE_DIR exists; skipping core creation"
+else
+    start-local-solr
+
+    echo "Creating core with:" "${@:1}"
+    /opt/solr/bin/solr create "${@:1}"
+
+    # See https://github.com/docker-solr/docker-solr/issues/27
+    echo "Checking core"
+    if ! wget -O - "http://localhost:${SOLR_PORT:-8983}/solr/admin/cores?action=STATUS" | grep instanceDir >/dev/null; then
+      echo "Could not find any cores"
+      exit 1
+    fi
+
+    echo "Created core with:" "${@:1}"
+    stop-local-solr
+
+    # check the core_dir exists; otherwise the detecting above will fail after stop/start
+    if [ ! -d "$CORE_DIR" ]; then
+        echo "Missing $CORE_DIR"
+        exit 1
+    fi
+fi
+
+exec solr-fg
diff --git a/8.11-slim/scripts/solr-demo b/8.11-slim/scripts/solr-demo
new file mode 100755
index 0000000..7d09d39
--- /dev/null
+++ b/8.11-slim/scripts/solr-demo
@@ -0,0 +1,42 @@
+#!/bin/bash
+#
+# Configure a Solr demo and then run solr in the foreground
+
+set -euo pipefail
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+. /opt/docker-solr/scripts/run-initdb
+
+CORE=demo
+
+coresdir=/var/solr/data
+CORE_DIR="$coresdir/demo"
+if [ -d "$CORE_DIR" ]; then
+  echo "$CORE_DIR exists; skipping demo creation"
+else
+  start-local-solr
+  echo "Creating $CORE"
+  /opt/solr/bin/solr create -c "$CORE"
+  echo "Created $CORE"
+  echo "Loading example data"
+  post_args=()
+  if [[ -n "${SOLR_PORT:-}" ]]; then
+    post_args+=(-p "$SOLR_PORT")
+  fi
+  /opt/solr/bin/post "${post_args[@]}" -c $CORE -commit no example/exampledocs/*.xml
+  /opt/solr/bin/post "${post_args[@]}" -c $CORE -commit no example/exampledocs/books.json
+  /opt/solr/bin/post "${post_args[@]}" -c $CORE -commit yes example/exampledocs/books.csv
+  echo "Loaded example data"
+  stop-local-solr
+
+    # check the core_dir exists; otherwise the detecting above will fail after stop/start
+    if [ ! -d "$CORE_DIR" ]; then
+        echo "Missing $CORE_DIR"
+        exit 1
+    fi
+fi
+
+exec solr-fg
diff --git a/8.11-slim/scripts/solr-fg b/8.11-slim/scripts/solr-fg
new file mode 100755
index 0000000..1a9132d
--- /dev/null
+++ b/8.11-slim/scripts/solr-fg
@@ -0,0 +1,57 @@
+#!/bin/bash
+#
+# start solr in the foreground
+set -e
+
+if [[ "$VERBOSE" == "yes" ]]; then
+    set -x
+fi
+
+EXTRA_ARGS=()
+EXTRA_ARGS+=('-Dlog4j2.formatMsgNoLookups=true')
+
+# Allow easy setting of the OOM behaviour
+# Test with: docker run -p 8983:8983 -it -e OOM=script -e SOLR_JAVA_MEM="-Xms25m -Xmx25m" solr
+if [[ -z "${OOM:-}" ]]; then
+  OOM='none'
+fi
+case "$OOM" in
+  'script')
+    EXTRA_ARGS+=(-a '-XX:OnOutOfMemoryError=/opt/docker-solr/scripts/oom_solr.sh')
+    ;;
+  'exit')
+    # recommended
+    EXTRA_ARGS+=(-a '-XX:+ExitOnOutOfMemoryError')
+    ;;
+  'crash')
+    EXTRA_ARGS+=(-a '-XX:+CrashOnOutOfMemoryError')
+    ;;
+  'none'|'')
+    ;;
+  *)
+   echo "Unsupported value in OOM=$OOM"
+   exit 1
+esac
+
+echo "Starting Solr $SOLR_VERSION"
+# determine TINI default. If it is already set, assume the user knows what they want
+if [[ -z "${TINI:-}" ]]; then
+  if [[ "$$" == 1 ]]; then
+    # Default to running tini, so we can run with an OOM script and have 'kill -9' work
+    TINI=yes
+  else
+    # Presumably we're already running under tini through 'docker --init', in which case we
+    # don't need to run it twice.
+    # It's also possible that we're run from a wrapper script without exec,
+    # in which case running tini would not be ideal either.
+    TINI=no
+  fi
+fi
+if [[ "$TINI" == yes ]]; then
+  exec /usr/bin/tini -- solr -f "$@" "${EXTRA_ARGS[@]}"
+elif [[ "$TINI" == no ]]; then
+  exec solr -f "$@" "${EXTRA_ARGS[@]}"
+else
+  echo "invalid value TINI=$TINI"
+  exit 1
+fi
diff --git a/8.11-slim/scripts/solr-foreground b/8.11-slim/scripts/solr-foreground
new file mode 100755
index 0000000..336104b
--- /dev/null
+++ b/8.11-slim/scripts/solr-foreground
@@ -0,0 +1,15 @@
+#!/bin/bash
+#
+# Run the initdb, then start solr in the foreground
+set -e
+
+if [[ "$VERBOSE" == "yes" ]]; then
+    set -x
+fi
+
+# init script for handling an empty /var/solr
+/opt/docker-solr/scripts/init-var-solr
+
+. /opt/docker-solr/scripts/run-initdb
+
+exec solr-fg "$@"
diff --git a/8.11-slim/scripts/solr-precreate b/8.11-slim/scripts/solr-precreate
new file mode 100755
index 0000000..5248203
--- /dev/null
+++ b/8.11-slim/scripts/solr-precreate
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Create a core on disk and then run solr in the foreground
+# arguments are: corename configdir
+# To simply create a core:
+#      docker run -P -d solr solr-precreate mycore
+# To create a core from mounted config:
+#      docker run -P -d -v $PWD/myconfig:/myconfig solr solr-precreate mycore /myconfig
+# To create a core in a mounted directory:
+#      mkdir myvarsolr; chown 8983:8983 myvarsolr
+#      docker run -it --rm -P -v $PWD/myvarsolr://var/solr solr solr-precreate mycore
+set -e
+
+echo "Executing $0" "$@"
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+# init script for handling an empty /var/solr
+/opt/docker-solr/scripts/init-var-solr
+
+. /opt/docker-solr/scripts/run-initdb
+
+/opt/docker-solr/scripts/precreate-core "$@"
+
+exec solr-fg
diff --git a/8.11-slim/scripts/start-local-solr b/8.11-slim/scripts/start-local-solr
new file mode 100755
index 0000000..4e20624
--- /dev/null
+++ b/8.11-slim/scripts/start-local-solr
@@ -0,0 +1,21 @@
+#!/bin/bash
+# configure Solr to run on the local interface, and start it running in the background
+
+set -euo pipefail
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+echo "Running solr in the background. Logs are in /var/solr/logs"
+SOLR_OPTS="-Djetty.host=${SOLR_LOCAL_HOST:-localhost} -Dlog4j2.formatMsgNoLookups=true" solr start
+max_try=${MAX_TRY:-12}
+wait_seconds=${WAIT_SECONDS:-5}
+if ! /opt/docker-solr/scripts/wait-for-solr.sh --max-attempts "$max_try" --wait-seconds "$wait_seconds"; then
+    echo "Could not start Solr."
+    if [ -f "/var/solr/logs/solr.log" ]; then
+        echo "Here is the log:"
+        cat "/var/solr/logs/solr.log"
+    fi
+    exit 1
+fi
diff --git a/8.11-slim/scripts/stop-local-solr b/8.11-slim/scripts/stop-local-solr
new file mode 100755
index 0000000..a0c7742
--- /dev/null
+++ b/8.11-slim/scripts/stop-local-solr
@@ -0,0 +1,11 @@
+#!/bin/bash
+# stop the background Solr, and restore the normal configuration
+
+set -e
+
+if [[ "$VERBOSE" == "yes" ]]; then
+    set -x
+fi
+
+echo "Shutting down the background Solr"
+solr stop
diff --git a/8.11-slim/scripts/wait-for-solr.sh b/8.11-slim/scripts/wait-for-solr.sh
new file mode 100755
index 0000000..42d25ac
--- /dev/null
+++ b/8.11-slim/scripts/wait-for-solr.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+#
+# A helper script to wait for solr
+#
+# Usage: wait-for-solr.sh [--max-attempts count] [--wait-seconds seconds] [--solr-url url]
+# Deprecated usage: wait-for-solr.sh [ max_attempts [ wait_seconds ] ]
+
+set -euo pipefail
+
+SCRIPT="$0"
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+function usage {
+  echo "$1"
+  echo "Usage: $SCRIPT [--max-attempts count] [--wait-seconds seconds ] [--solr-url url]"
+  exit 1
+}
+
+max_attempts=12
+wait_seconds=5
+
+if [[ -v SOLR_PORT ]] && ! grep -E -q '^[0-9]+$' <<<"$SOLR_PORT"; then
+  echo "Invalid SOLR_PORT=$SOLR_PORT environment variable specified"
+  exit 1
+fi
+
+solr_url="http://localhost:${SOLR_PORT:-8983}"
+
+while (( $# > 0 )); do
+  case "$1" in
+   --help)
+     cat <<EOM
+Usage: $SCRIPT [options]
+
+Options:
+  --max-attempts count: number of attempts to check Solr is up. Default: $max_attempts
+  --wait-seconds seconds: number of seconds to wait between attempts. Default: $wait_seconds
+  --solr-url url: URL for Solr server to check. Default: $solr_url
+EOM
+     exit 0
+     ;;
+   --solr-url)
+     solr_url="$2";
+     shift 2
+     ;;
+
+   --max-attempts)
+     max_attempts="$2";
+     shift 2;
+     ;;
+
+   --wait-seconds)
+     wait_seconds="$2";
+     shift 2;
+     ;;
+
+  * )
+    # deprecated invocation, kept for backwards compatibility
+    max_attempts=$1;
+    wait_seconds=$2;
+    echo "WARNING: deprecated invocation. Use $SCRIPT [--max-attempts count] [--wait-seconds seconds]"
+    shift 2;
+    break;
+    ;;
+
+  esac
+done
+
+grep -q -E '^[0-9]+$' <<<"$max_attempts" || usage "--max-attempts $max_attempts: not a number"
+if (( max_attempts == 0 )); then
+  echo "The --max-attempts argument should be >0"
+  exit 1
+fi
+grep -q -E '^[0-9]+$' <<<"$wait_seconds" || usage "--wait-seconds $wait_seconds: not a number"
+grep -q -E '^https?://' <<<"$solr_url" || usage "--solr-url $solr_url: not a URL"
+
+((attempts_left=max_attempts))
+while (( attempts_left > 0 )); do
+  if wget -q -O - "$solr_url" | grep -i solr >/dev/null; then
+    break
+  fi
+  (( attempts_left-- ))
+  if (( attempts_left == 0 )); then
+    echo "Solr is still not running; giving up"
+    exit 1
+  fi
+  if (( attempts_left == 1 )); then
+    attempts=attempt
+  else
+    attempts=attempts
+  fi
+  echo "Solr is not running yet on $solr_url. $attempts_left $attempts left"
+  sleep "$wait_seconds"
+done
+echo "Solr is running on $solr_url"
diff --git a/8.11-slim/scripts/wait-for-zookeeper.sh b/8.11-slim/scripts/wait-for-zookeeper.sh
new file mode 100755
index 0000000..82366a4
--- /dev/null
+++ b/8.11-slim/scripts/wait-for-zookeeper.sh
@@ -0,0 +1,165 @@
+#!/bin/bash
+#
+# A helper script to wait for ZooKeeper
+#
+# This script waits for a ZooKeeper master to appear.
+# It repeatedly looks up the name passed as argument
+# in the DNS using getent, and then connects to the
+# ZooKeeper admin port and uses the 'srvr' command to
+# obtain the server's status.
+# You can use this in a Kubernetes init container to
+# delay Solr pods starting until the ZooKeeper service
+# has settled down. Or you could explicitly run this in
+# the Solr container before exec'ing Solr.
+#
+# Inspired by https://github.com/helm/charts/blob/9eba7b1c80990233a68dce48f4a8fe0baf9b7fa5/incubator/solr/templates/statefulset.yaml#L60
+#
+# Usage: wait-for-zookeeper.sh [--max-attempts count] [--wait-seconds seconds] zookeeper-service-name
+#
+# If no argument is provided, but a Solr-style ZK_HOST is set,
+# that will be used. If neither is provided, the default
+# name is 'solr-zookeeper-headless', to match the helm chart.
+
+set -euo pipefail
+
+SCRIPT="$0"
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+function usage {
+  echo "$1"
+  echo "Usage: $SCRIPT [--max-attempts count] [--wait-seconds seconds ] zookeeper-service-name"
+  exit 1
+}
+
+TMP_HOSTS="/tmp/hosts.$$"
+TMP_STATUS="/tmp/status.$$"
+
+function cleanup {
+    rm -f $TMP_HOSTS $TMP_STATUS
+}
+
+trap cleanup EXIT
+
+function check_zookeeper {
+    local host=$1
+    local port="${2:-2181}"
+    if ! echo srvr | nc "$host" "$port" > $TMP_STATUS; then
+        echo "Failed to get status from $host"
+        return
+    fi
+    if [ ! -s $TMP_STATUS ]; then
+        echo "No data from $ip"
+        return
+    fi
+    if grep -q 'not currently serving requests' $TMP_STATUS; then
+        echo "Node $ip is not currently serving requests"
+        return
+    fi
+    mode=$(grep "Mode: " $TMP_STATUS | sed 's/Mode: //');
+    if [ -z "$mode" ]; then
+        echo "Cannot determine mode from:"
+        cat $TMP_STATUS
+        return
+    fi
+    echo "Node $ip is a $mode"
+    if [ "$mode" = "leader" ] || [ "$mode" = "standalone" ]; then
+        echo "Done"
+        exit 0
+    fi
+}
+
+max_attempts=120
+wait_seconds=2
+while (( $# > 0 )); do
+  case "$1" in
+   --help)
+     cat <<EOM
+Usage: $SCRIPT [options] zookeeper-service-name
+
+Options:
+  --max-attempts count: number of attempts to check Solr is up. Default: $max_attempts
+  --wait-seconds seconds: number of seconds to wait between attempts. Default: $wait_seconds
+EOM
+     exit 0
+     ;;
+
+   --max-attempts)
+     max_attempts="$2";
+     shift 2;
+     ;;
+
+   --wait-seconds)
+     wait_seconds="$2";
+     shift 2;
+     ;;
+
+   *)
+    if [ -n "${lookup_arg:-}" ]; then
+      usage "Cannot specify multiple zookeeper service names"
+    fi
+    lookup_arg=$1;
+    shift;
+    break;
+    ;;
+
+  esac
+done
+
+grep -q -E '^[0-9]+$' <<<"$max_attempts" || usage "--max-attempts $max_attempts: not a number"
+if (( max_attempts == 0 )); then
+  echo "The --max-attempts argument should be >0"
+  exit 1
+fi
+grep -q -E '^[0-9]+$' <<<"$wait_seconds" || usage "--wait-seconds $wait_seconds: not a number"
+
+if [ -z "${lookup_arg:-}" ]; then
+  if [ -n "$ZK_HOST" ]; then
+    lookup_arg="$ZK_HOST"
+  else
+    lookup_arg=solr-zookeeper-headless
+  fi
+fi
+
+echo "Looking up '$lookup_arg'"
+# split on commas, for when a ZK_HOST string like zoo1:2181,zoo2:2181 is used
+IFS=',' read -ra lookups <<< "$lookup_arg"
+((attempts_left=max_attempts))
+while (( attempts_left > 0 )); do
+  for lookup in "${lookups[@]}"; do
+    if grep -q -E "^\[[0-9].*\]" <<<"$lookup"; then
+      # looks like an IPv6 address, eg [2001:DB8::1] or [2001:DB8::1]:2181
+      # getent does not support the bracket notation, but does support IPv6 addresses
+      host=$(sed -E 's/\[(.*)\].*/\1/' <<<"$lookup")
+      port=$(sed -E 's/^\[(.*)\]:?//' <<<"$lookup")
+    else
+      # IPv4, just split on :
+      IFS=: read -ra split <<<"$lookup"
+      host="${split[0]}"
+      port="${split[1]:-}"
+    fi
+    if [[ "${VERBOSE:-}" == "yes" ]]; then
+      echo "Parsed host=$host port=${port:-}"
+    fi
+    if getent hosts "$host" > $TMP_HOSTS; then
+      while read -r ip hostname ; do
+        echo "${hostname:-}">/dev/null # consume for shellcheck
+        check_zookeeper "$ip" "$port"
+      done <$TMP_HOSTS
+    else
+      echo "Cannot find $lookup yet"
+    fi
+  done
+  (( attempts_left-- ))
+  if (( attempts_left == 0 )); then echo "Still no master found; giving up"
+    exit 1
+  fi
+  sleep "$wait_seconds"
+done
+
+# To test the parsing:
+#  bash scripts/wait-for-zookeeper.sh foo
+#  bash scripts/wait-for-zookeeper.sh 'ZK_HOST=[2001:DB8::1]:2181,[2001:DB8::1],127.0.0.1:2181,127.0.0.2'
+#  ZK_HOST=[2001:DB8::1]:2181,[2001:DB8::1],127.0.0.1:2181,127.0.0.2 bash scripts/wait-for-zookeeper.sh
diff --git a/8.11/Dockerfile b/8.11/Dockerfile
new file mode 100644
index 0000000..a9e1a3e
--- /dev/null
+++ b/8.11/Dockerfile
@@ -0,0 +1,124 @@
+
+FROM openjdk:11-jre
+
+LABEL maintainer="The Apache Lucene/Solr Project"
+LABEL repository="https://github.com/docker-solr/docker-solr"
+
+ARG SOLR_VERSION="8.11.2"
+ARG SOLR_SHA512="22fedcc0090eda72c3c5a5ea769c93adaf7d92c5c4479993f009ef0b9d42de5bd2ed1e0565ca01f3428587d8a3836286aa3017aab157050f2bd5bc3482fdebaa"
+ARG SOLR_KEYS="86EDB9C33B8517228E88A8F93E48C0C6EF362B9E"
+# If specified, this will override SOLR_DOWNLOAD_SERVER and all ASF mirrors. Typically used downstream for custom builds
+ARG SOLR_DOWNLOAD_URL
+
+# Override the solr download location with e.g.:
+#   docker build -t mine --build-arg SOLR_DOWNLOAD_SERVER=http://www-eu.apache.org/dist/lucene/solr .
+ARG SOLR_DOWNLOAD_SERVER
+
+RUN set -ex; \
+  apt-get update; \
+  apt-get -y install acl dirmngr gpg lsof procps wget netcat gosu tini; \
+  rm -rf /var/lib/apt/lists/*; \
+  cd /usr/local/bin; wget -nv https://github.com/apangin/jattach/releases/download/v1.5/jattach; chmod 755 jattach; \
+  echo >jattach.sha512 "d8eedbb3e192a8596c08efedff99b9acf1075331e1747107c07cdb1718db2abe259ef168109e46bd4cf80d47d43028ff469f95e6ddcbdda4d7ffa73a20e852f9  jattach"; \
+  sha512sum -c jattach.sha512; rm jattach.sha512
+
+ENV SOLR_USER="solr" \
+    SOLR_UID="8983" \
+    SOLR_GROUP="solr" \
+    SOLR_GID="8983" \
+    SOLR_CLOSER_URL="http://www.apache.org/dyn/closer.lua?filename=lucene/solr/$SOLR_VERSION/solr-$SOLR_VERSION.tgz&action=download" \
+    SOLR_DIST_URL="https://www.apache.org/dist/lucene/solr/$SOLR_VERSION/solr-$SOLR_VERSION.tgz" \
+    SOLR_ARCHIVE_URL="https://archive.apache.org/dist/lucene/solr/$SOLR_VERSION/solr-$SOLR_VERSION.tgz" \
+    PATH="/opt/solr/bin:/opt/docker-solr/scripts:$PATH" \
+    SOLR_INCLUDE=/etc/default/solr.in.sh \
+    SOLR_HOME=/var/solr/data \
+    SOLR_PID_DIR=/var/solr \
+    SOLR_LOGS_DIR=/var/solr/logs \
+    LOG4J_PROPS=/var/solr/log4j2.xml
+
+RUN set -ex; \
+  groupadd -r --gid "$SOLR_GID" "$SOLR_GROUP"; \
+  useradd -r --uid "$SOLR_UID" --gid "$SOLR_GID" "$SOLR_USER"
+
+RUN set -ex; \
+  export GNUPGHOME="/tmp/gnupg_home"; \
+  mkdir -p "$GNUPGHOME"; \
+  chmod 700 "$GNUPGHOME"; \
+  echo "disable-ipv6" >> "$GNUPGHOME/dirmngr.conf"; \
+  for key in $SOLR_KEYS; do \
+    found=''; \
+    for server in \
+      ha.pool.sks-keyservers.net \
+      hkp://keyserver.ubuntu.com:80 \
+      hkp://p80.pool.sks-keyservers.net:80 \
+      pgp.mit.edu \
+    ; do \
+      echo "  trying $server for $key"; \
+      gpg --batch --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$key" && found=yes && break; \
+      gpg --batch --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$key" && found=yes && break; \
+    done; \
+    test -z "$found" && echo >&2 "error: failed to fetch $key from several disparate servers -- network issues?" && exit 1; \
+  done; \
+  exit 0
+
+RUN set -ex; \
+  export GNUPGHOME="/tmp/gnupg_home"; \
+  MAX_REDIRECTS=1; \
+  if [ -n "$SOLR_DOWNLOAD_URL" ]; then \
+    # If a custom URL is defined, we download from non-ASF mirror URL and allow more redirects and skip GPG step
+    # This takes effect only if the SOLR_DOWNLOAD_URL build-arg is specified, typically in downstream Dockerfiles
+    MAX_REDIRECTS=4; \
+    SKIP_GPG_CHECK=true; \
+  elif [ -n "$SOLR_DOWNLOAD_SERVER" ]; then \
+    SOLR_DOWNLOAD_URL="$SOLR_DOWNLOAD_SERVER/$SOLR_VERSION/solr-$SOLR_VERSION.tgz"; \
+  fi; \
+  for url in $SOLR_DOWNLOAD_URL $SOLR_CLOSER_URL $SOLR_DIST_URL $SOLR_ARCHIVE_URL; do \
+    if [ -f "/opt/solr-$SOLR_VERSION.tgz" ]; then break; fi; \
+    echo "downloading $url"; \
+    if wget -t 10 --max-redirect $MAX_REDIRECTS --retry-connrefused -nv "$url" -O "/opt/solr-$SOLR_VERSION.tgz"; then break; else rm -f "/opt/solr-$SOLR_VERSION.tgz"; fi; \
+  done; \
+  if [ ! -f "/opt/solr-$SOLR_VERSION.tgz" ]; then echo "failed all download attempts for solr-$SOLR_VERSION.tgz"; exit 1; fi; \
+  if [ -z "$SKIP_GPG_CHECK" ]; then \
+    echo "downloading $SOLR_ARCHIVE_URL.asc"; \
+    wget -nv "$SOLR_ARCHIVE_URL.asc" -O "/opt/solr-$SOLR_VERSION.tgz.asc"; \
+    echo "$SOLR_SHA512 */opt/solr-$SOLR_VERSION.tgz" | sha512sum -c -; \
+    (>&2 ls -l "/opt/solr-$SOLR_VERSION.tgz" "/opt/solr-$SOLR_VERSION.tgz.asc"); \
+    gpg --batch --verify "/opt/solr-$SOLR_VERSION.tgz.asc" "/opt/solr-$SOLR_VERSION.tgz"; \
+  else \
+    echo "Skipping GPG validation due to non-Apache build"; \
+  fi; \
+  tar -C /opt --extract --file "/opt/solr-$SOLR_VERSION.tgz"; \
+  (cd /opt; ln -s "solr-$SOLR_VERSION" solr); \
+  rm "/opt/solr-$SOLR_VERSION.tgz"*; \
+  rm -Rf /opt/solr/docs/ /opt/solr/dist/{solr-core-$SOLR_VERSION.jar,solr-solrj-$SOLR_VERSION.jar,solrj-lib,solr-test-framework-$SOLR_VERSION.jar,test-framework}; \
+  mkdir -p /opt/solr/server/solr/lib /docker-entrypoint-initdb.d /opt/docker-solr; \
+  chown -R 0:0 "/opt/solr-$SOLR_VERSION"; \
+  find "/opt/solr-$SOLR_VERSION" -type d -print0 | xargs -0 chmod 0755; \
+  find "/opt/solr-$SOLR_VERSION" -type f -print0 | xargs -0 chmod 0644; \
+  chmod -R 0755 "/opt/solr-$SOLR_VERSION/bin" "/opt/solr-$SOLR_VERSION/contrib/prometheus-exporter/bin/solr-exporter" /opt/solr-$SOLR_VERSION/server/scripts/cloud-scripts; \
+  cp /opt/solr/bin/solr.in.sh /etc/default/solr.in.sh; \
+  mv /opt/solr/bin/solr.in.sh /opt/solr/bin/solr.in.sh.orig; \
+  mv /opt/solr/bin/solr.in.cmd /opt/solr/bin/solr.in.cmd.orig; \
+  chown root:0 /etc/default/solr.in.sh; \
+  chmod 0664 /etc/default/solr.in.sh; \
+  mkdir -p /var/solr/data /var/solr/logs; \
+  (cd /opt/solr/server/solr; cp solr.xml zoo.cfg /var/solr/data/); \
+  cp /opt/solr/server/resources/log4j2.xml /var/solr/log4j2.xml; \
+  find /var/solr -type d -print0 | xargs -0 chmod 0770; \
+  find /var/solr -type f -print0 | xargs -0 chmod 0660; \
+  sed -i -e "s/\"\$(whoami)\" == \"root\"/\$(id -u) == 0/" /opt/solr/bin/solr; \
+  sed -i -e 's/lsof -PniTCP:/lsof -t -PniTCP:/' /opt/solr/bin/solr; \
+  chown -R "0:0" /opt/solr-$SOLR_VERSION /docker-entrypoint-initdb.d /opt/docker-solr; \
+  chown -R "$SOLR_USER:0" /var/solr; \
+  { command -v gpgconf; gpgconf --kill all || :; }; \
+  rm -r "$GNUPGHOME"
+
+COPY --chown=0:0 scripts /opt/docker-solr/scripts
+
+VOLUME /var/solr
+EXPOSE 8983
+WORKDIR /opt/solr
+USER $SOLR_USER
+
+ENTRYPOINT ["docker-entrypoint.sh"]
+CMD ["solr-foreground"]
diff --git a/8.11/scripts/docker-entrypoint.sh b/8.11/scripts/docker-entrypoint.sh
new file mode 100755
index 0000000..0610049
--- /dev/null
+++ b/8.11/scripts/docker-entrypoint.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# docker-entrypoint for docker-solr
+
+set -e
+
+# Clear some variables that we don't want runtime
+unset SOLR_USER SOLR_UID SOLR_GROUP SOLR_GID \
+      SOLR_CLOSER_URL SOLR_DIST_URL SOLR_ARCHIVE_URL SOLR_DOWNLOAD_URL SOLR_DOWNLOAD_SERVER SOLR_KEYS SOLR_SHA512
+
+if [[ "$VERBOSE" == "yes" ]]; then
+    set -x
+fi
+
+if [[ -v SOLR_PORT ]] && ! grep -E -q '^[0-9]+$' <<<"${SOLR_PORT:-}"; then
+  SOLR_PORT=8983
+  export SOLR_PORT
+fi
+
+# when invoked with e.g.: docker run solr -help
+if [ "${1:0:1}" == '-' ]; then
+    set -- solr-foreground "$@"
+fi
+
+# execute command passed in as arguments.
+# The Dockerfile has specified the PATH to include
+# /opt/solr/bin (for Solr) and /opt/docker-solr/scripts (for our scripts
+# like solr-foreground, solr-create, solr-precreate, solr-demo).
+# Note: if you specify "solr", you'll typically want to add -f to run it in
+# the foreground.
+exec "$@"
diff --git a/8.11/scripts/init-var-solr b/8.11/scripts/init-var-solr
new file mode 100755
index 0000000..8a29de1
--- /dev/null
+++ b/8.11/scripts/init-var-solr
@@ -0,0 +1,61 @@
+#!/bin/bash
+#
+# A helper script to initialise an empty $DIR
+# If you use volumes then Docker will copy the $DIR content from the container to the volume.
+# If you use bind mounts, that does not happen, so we do it here.
+
+set -e
+
+if [[ "$VERBOSE" == "yes" ]]; then
+    set -x
+fi
+
+if [[ -n "${NO_INIT_VAR_SOLR:-}" ]]; then
+    exit 0
+fi
+
+DIR=${1:-/var/solr}
+
+if [ ! -d "$DIR" ]; then
+    echo "Missing $DIR"
+    exit 1
+fi
+
+function check_dir_writability {
+    local dir="$1"
+    if [ ! -w "$dir" ]; then
+        echo "Cannot write to $dir as $(id -u):$(id -g)"
+        ls -ld "$dir"
+        exit 1
+    fi
+}
+
+if [ ! -d "$DIR/data" ]; then
+    echo "Creating $DIR/data"
+    check_dir_writability "$DIR"
+    mkdir "$DIR/data"
+    chmod 0770 "$DIR/data"
+fi
+
+if [ ! -d "$DIR/logs" ]; then
+    echo "Creating $DIR/logs"
+    check_dir_writability "$DIR"
+    mkdir "$DIR/logs"
+    chmod 0770 "$DIR/logs"
+fi
+
+if [ ! -f "$DIR/data/solr.xml" ]; then
+    echo "Copying solr.xml"
+    cp -a /opt/solr/server/solr/solr.xml "$DIR/data/solr.xml"
+fi
+
+if [ ! -f "$DIR/data/zoo.cfg" ]; then
+    echo "Copying zoo.cfg"
+    cp -a /opt/solr/server/solr/zoo.cfg "$DIR/data/zoo.cfg"
+fi
+
+if [ ! -f "$DIR/log4j2.xml" ]; then
+    echo "Copying log4j2.xml"
+    cp -a /opt/solr/server/resources/log4j2.xml "$DIR/log4j2.xml"
+fi
+
diff --git a/8.11/scripts/oom_solr.sh b/8.11/scripts/oom_solr.sh
new file mode 100755
index 0000000..bed5095
--- /dev/null
+++ b/8.11/scripts/oom_solr.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+# Custom oom handler loosely based on
+# https://github.com/apache/lucene-solr/blob/master/solr/bin/oom_solr.sh
+# See solr-forgeground for how to configure OOM behaviour
+
+if [[ -z "${SOLR_LOGS_DIR:-}" ]]; then
+    if [ -d /var/solr/logs ]; then
+        SOLR_LOGS_DIR=/var/solr/logs
+    elif [ -d /opt/solr/server/logs ]; then
+        SOLR_LOGS_DIR=/opt/solr/server/logs
+    else
+        echo "Cannot determine SOLR_LOGS_DIR!"
+        exit 1
+    fi
+fi
+SOLR_PID=$(pgrep -f start.jar)
+if [[ -z "$SOLR_PID" ]]; then
+  echo "Couldn't find Solr process running!"
+  exit
+fi
+
+NOW=$(date +"%F_%H_%M_%S")
+(
+echo "Running OOM killer script for Solr process $SOLR_PID"
+if [[ "$SOLR_PID" == 1 ]]; then
+  # under Docker, when running as pid 1, a SIGKILL is ignored,
+  # so use the default SIGTERM
+  kill "$SOLR_PID"
+  sleep 2
+  # if that hasn't worked, send SIGKILL
+  kill -SIGILL "$SOLR_PID"
+else
+  # if we're running with `--init` or under tini or similar,
+  # follow the upstream behaviour
+  kill -9 "$SOLR_PID"
+fi
+) | tee "$SOLR_LOGS_DIR/solr_oom_killer-$SOLR_PORT-$NOW.log"
diff --git a/8.11/scripts/precreate-core b/8.11/scripts/precreate-core
new file mode 100755
index 0000000..bca0f48
--- /dev/null
+++ b/8.11/scripts/precreate-core
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Create a core on disk
+# arguments are: corename configdir
+
+set -e
+
+echo "Executing $0" "$@"
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+CORE=${1:-gettingstarted}
+CONFIG_SOURCE="${2:-}"
+if [[ -z "$CONFIG_SOURCE" ]]; then
+    DEFAULT_CONFIGS=(_default data_driven_schema_configs)
+    for config_dir in "${DEFAULT_CONFIGS[@]}"; do
+        config_dir="/opt/solr/server/solr/configsets/$config_dir"
+        if [ -d "$config_dir" ]; then
+           CONFIG_SOURCE="$config_dir"
+           break
+        fi
+    done
+    if [[ -z $CONFIG_SOURCE ]]; then
+        echo "Cannot find default config"
+        exit 1
+    fi
+fi
+
+coresdir=/var/solr/data
+
+coredir="$coresdir/$CORE"
+if [[ ! -d $coredir ]]; then
+    cp -r "$CONFIG_SOURCE/" "$coredir"
+    touch "$coredir/core.properties"
+    echo "Created $CORE"
+else
+    echo "Core $CORE already exists"
+fi
diff --git a/8.11/scripts/run-initdb b/8.11/scripts/run-initdb
new file mode 100755
index 0000000..5f250c9
--- /dev/null
+++ b/8.11/scripts/run-initdb
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# Run the init-solr-home script and source any '.sh' scripts in
+# /docker-entrypoint-initdb.d.
+# This script is sourced by some of the solr-* commands, so that
+# you can run eg:
+#
+#   mkdir initdb; echo "echo hi" > initdb/hi.sh
+#   docker run -v $PWD/initdb:/docker-entrypoint-initdb.d solr
+#
+# and have your script execute before Solr starts.
+#
+# Note: scripts can modify the environment, which will affect
+# subsequent scripts and ultimately Solr. That allows you to set
+# environment variables from your scripts (though you usually just
+# use "docker run -e"). If this is undesirable in your use-case,
+# have your scripts execute a sub-shell.
+
+set -e
+
+# execute files in /docker-entrypoint-initdb.d before starting solr
+while read -r f; do
+    case "$f" in
+        *.sh)     echo "$0: running $f"; . "$f" ;;
+        *)        echo "$0: ignoring $f" ;;
+    esac
+    echo
+done < <(find /docker-entrypoint-initdb.d/ -mindepth 1 -type f | sort -n)
diff --git a/8.11/scripts/solr-create b/8.11/scripts/solr-create
new file mode 100755
index 0000000..e2054a1
--- /dev/null
+++ b/8.11/scripts/solr-create
@@ -0,0 +1,66 @@
+#!/bin/bash
+#
+# This script starts Solr on localhost, creates a core with "solr create",
+# stops Solr, and then starts Solr as normal.
+# Any arguments are passed to the "solr create".
+# To simply create a core:
+#      docker run -P -d solr solr-create -c mycore
+# To create a core from mounted config:
+#      docker run -P -d -v $PWD/myconfig:/myconfig solr solr-create -c mycore -d /myconfig
+
+set -euo pipefail
+echo "Executing $0" "$@"
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+# init script for handling an empty /var/solr
+/opt/docker-solr/scripts/init-var-solr
+
+. /opt/docker-solr/scripts/run-initdb
+
+# solr uses "-c corename". Parse the arguments to determine the core name.
+CORE_NAME="$(
+  while (( $# > 0 )); do
+    if [[ "$1" == '-c' ]]; then
+      shift
+      echo "$1"
+    fi
+    shift
+  done
+)"
+if [[ -z "${CORE_NAME:-}" ]]; then
+  echo "Could not determine core name"
+  exit 1
+fi
+
+coresdir=/var/solr/data
+CORE_DIR="$coresdir/$CORE_NAME"
+
+if [[ -d $CORE_DIR ]]; then
+    echo "Directory $CORE_DIR exists; skipping core creation"
+else
+    start-local-solr
+
+    echo "Creating core with:" "${@:1}"
+    /opt/solr/bin/solr create "${@:1}"
+
+    # See https://github.com/docker-solr/docker-solr/issues/27
+    echo "Checking core"
+    if ! wget -O - "http://localhost:${SOLR_PORT:-8983}/solr/admin/cores?action=STATUS" | grep instanceDir >/dev/null; then
+      echo "Could not find any cores"
+      exit 1
+    fi
+
+    echo "Created core with:" "${@:1}"
+    stop-local-solr
+
+    # check the core_dir exists; otherwise the detecting above will fail after stop/start
+    if [ ! -d "$CORE_DIR" ]; then
+        echo "Missing $CORE_DIR"
+        exit 1
+    fi
+fi
+
+exec solr-fg
diff --git a/8.11/scripts/solr-demo b/8.11/scripts/solr-demo
new file mode 100755
index 0000000..7d09d39
--- /dev/null
+++ b/8.11/scripts/solr-demo
@@ -0,0 +1,42 @@
+#!/bin/bash
+#
+# Configure a Solr demo and then run solr in the foreground
+
+set -euo pipefail
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+. /opt/docker-solr/scripts/run-initdb
+
+CORE=demo
+
+coresdir=/var/solr/data
+CORE_DIR="$coresdir/demo"
+if [ -d "$CORE_DIR" ]; then
+  echo "$CORE_DIR exists; skipping demo creation"
+else
+  start-local-solr
+  echo "Creating $CORE"
+  /opt/solr/bin/solr create -c "$CORE"
+  echo "Created $CORE"
+  echo "Loading example data"
+  post_args=()
+  if [[ -n "${SOLR_PORT:-}" ]]; then
+    post_args+=(-p "$SOLR_PORT")
+  fi
+  /opt/solr/bin/post "${post_args[@]}" -c $CORE -commit no example/exampledocs/*.xml
+  /opt/solr/bin/post "${post_args[@]}" -c $CORE -commit no example/exampledocs/books.json
+  /opt/solr/bin/post "${post_args[@]}" -c $CORE -commit yes example/exampledocs/books.csv
+  echo "Loaded example data"
+  stop-local-solr
+
+    # check the core_dir exists; otherwise the detecting above will fail after stop/start
+    if [ ! -d "$CORE_DIR" ]; then
+        echo "Missing $CORE_DIR"
+        exit 1
+    fi
+fi
+
+exec solr-fg
diff --git a/8.11/scripts/solr-fg b/8.11/scripts/solr-fg
new file mode 100755
index 0000000..1a9132d
--- /dev/null
+++ b/8.11/scripts/solr-fg
@@ -0,0 +1,57 @@
+#!/bin/bash
+#
+# start solr in the foreground
+set -e
+
+if [[ "$VERBOSE" == "yes" ]]; then
+    set -x
+fi
+
+EXTRA_ARGS=()
+EXTRA_ARGS+=('-Dlog4j2.formatMsgNoLookups=true')
+
+# Allow easy setting of the OOM behaviour
+# Test with: docker run -p 8983:8983 -it -e OOM=script -e SOLR_JAVA_MEM="-Xms25m -Xmx25m" solr
+if [[ -z "${OOM:-}" ]]; then
+  OOM='none'
+fi
+case "$OOM" in
+  'script')
+    EXTRA_ARGS+=(-a '-XX:OnOutOfMemoryError=/opt/docker-solr/scripts/oom_solr.sh')
+    ;;
+  'exit')
+    # recommended
+    EXTRA_ARGS+=(-a '-XX:+ExitOnOutOfMemoryError')
+    ;;
+  'crash')
+    EXTRA_ARGS+=(-a '-XX:+CrashOnOutOfMemoryError')
+    ;;
+  'none'|'')
+    ;;
+  *)
+   echo "Unsupported value in OOM=$OOM"
+   exit 1
+esac
+
+echo "Starting Solr $SOLR_VERSION"
+# determine TINI default. If it is already set, assume the user knows what they want
+if [[ -z "${TINI:-}" ]]; then
+  if [[ "$$" == 1 ]]; then
+    # Default to running tini, so we can run with an OOM script and have 'kill -9' work
+    TINI=yes
+  else
+    # Presumably we're already running under tini through 'docker --init', in which case we
+    # don't need to run it twice.
+    # It's also possible that we're run from a wrapper script without exec,
+    # in which case running tini would not be ideal either.
+    TINI=no
+  fi
+fi
+if [[ "$TINI" == yes ]]; then
+  exec /usr/bin/tini -- solr -f "$@" "${EXTRA_ARGS[@]}"
+elif [[ "$TINI" == no ]]; then
+  exec solr -f "$@" "${EXTRA_ARGS[@]}"
+else
+  echo "invalid value TINI=$TINI"
+  exit 1
+fi
diff --git a/8.11/scripts/solr-foreground b/8.11/scripts/solr-foreground
new file mode 100755
index 0000000..336104b
--- /dev/null
+++ b/8.11/scripts/solr-foreground
@@ -0,0 +1,15 @@
+#!/bin/bash
+#
+# Run the initdb, then start solr in the foreground
+set -e
+
+if [[ "$VERBOSE" == "yes" ]]; then
+    set -x
+fi
+
+# init script for handling an empty /var/solr
+/opt/docker-solr/scripts/init-var-solr
+
+. /opt/docker-solr/scripts/run-initdb
+
+exec solr-fg "$@"
diff --git a/8.11/scripts/solr-precreate b/8.11/scripts/solr-precreate
new file mode 100755
index 0000000..5248203
--- /dev/null
+++ b/8.11/scripts/solr-precreate
@@ -0,0 +1,27 @@
+#!/bin/bash
+#
+# Create a core on disk and then run solr in the foreground
+# arguments are: corename configdir
+# To simply create a core:
+#      docker run -P -d solr solr-precreate mycore
+# To create a core from mounted config:
+#      docker run -P -d -v $PWD/myconfig:/myconfig solr solr-precreate mycore /myconfig
+# To create a core in a mounted directory:
+#      mkdir myvarsolr; chown 8983:8983 myvarsolr
+#      docker run -it --rm -P -v $PWD/myvarsolr://var/solr solr solr-precreate mycore
+set -e
+
+echo "Executing $0" "$@"
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+# init script for handling an empty /var/solr
+/opt/docker-solr/scripts/init-var-solr
+
+. /opt/docker-solr/scripts/run-initdb
+
+/opt/docker-solr/scripts/precreate-core "$@"
+
+exec solr-fg
diff --git a/8.11/scripts/start-local-solr b/8.11/scripts/start-local-solr
new file mode 100755
index 0000000..4e20624
--- /dev/null
+++ b/8.11/scripts/start-local-solr
@@ -0,0 +1,21 @@
+#!/bin/bash
+# configure Solr to run on the local interface, and start it running in the background
+
+set -euo pipefail
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+echo "Running solr in the background. Logs are in /var/solr/logs"
+SOLR_OPTS="-Djetty.host=${SOLR_LOCAL_HOST:-localhost} -Dlog4j2.formatMsgNoLookups=true" solr start
+max_try=${MAX_TRY:-12}
+wait_seconds=${WAIT_SECONDS:-5}
+if ! /opt/docker-solr/scripts/wait-for-solr.sh --max-attempts "$max_try" --wait-seconds "$wait_seconds"; then
+    echo "Could not start Solr."
+    if [ -f "/var/solr/logs/solr.log" ]; then
+        echo "Here is the log:"
+        cat "/var/solr/logs/solr.log"
+    fi
+    exit 1
+fi
diff --git a/8.11/scripts/stop-local-solr b/8.11/scripts/stop-local-solr
new file mode 100755
index 0000000..a0c7742
--- /dev/null
+++ b/8.11/scripts/stop-local-solr
@@ -0,0 +1,11 @@
+#!/bin/bash
+# stop the background Solr, and restore the normal configuration
+
+set -e
+
+if [[ "$VERBOSE" == "yes" ]]; then
+    set -x
+fi
+
+echo "Shutting down the background Solr"
+solr stop
diff --git a/8.11/scripts/wait-for-solr.sh b/8.11/scripts/wait-for-solr.sh
new file mode 100755
index 0000000..42d25ac
--- /dev/null
+++ b/8.11/scripts/wait-for-solr.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+#
+# A helper script to wait for solr
+#
+# Usage: wait-for-solr.sh [--max-attempts count] [--wait-seconds seconds] [--solr-url url]
+# Deprecated usage: wait-for-solr.sh [ max_attempts [ wait_seconds ] ]
+
+set -euo pipefail
+
+SCRIPT="$0"
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+function usage {
+  echo "$1"
+  echo "Usage: $SCRIPT [--max-attempts count] [--wait-seconds seconds ] [--solr-url url]"
+  exit 1
+}
+
+max_attempts=12
+wait_seconds=5
+
+if [[ -v SOLR_PORT ]] && ! grep -E -q '^[0-9]+$' <<<"$SOLR_PORT"; then
+  echo "Invalid SOLR_PORT=$SOLR_PORT environment variable specified"
+  exit 1
+fi
+
+solr_url="http://localhost:${SOLR_PORT:-8983}"
+
+while (( $# > 0 )); do
+  case "$1" in
+   --help)
+     cat <<EOM
+Usage: $SCRIPT [options]
+
+Options:
+  --max-attempts count: number of attempts to check Solr is up. Default: $max_attempts
+  --wait-seconds seconds: number of seconds to wait between attempts. Default: $wait_seconds
+  --solr-url url: URL for Solr server to check. Default: $solr_url
+EOM
+     exit 0
+     ;;
+   --solr-url)
+     solr_url="$2";
+     shift 2
+     ;;
+
+   --max-attempts)
+     max_attempts="$2";
+     shift 2;
+     ;;
+
+   --wait-seconds)
+     wait_seconds="$2";
+     shift 2;
+     ;;
+
+  * )
+    # deprecated invocation, kept for backwards compatibility
+    max_attempts=$1;
+    wait_seconds=$2;
+    echo "WARNING: deprecated invocation. Use $SCRIPT [--max-attempts count] [--wait-seconds seconds]"
+    shift 2;
+    break;
+    ;;
+
+  esac
+done
+
+grep -q -E '^[0-9]+$' <<<"$max_attempts" || usage "--max-attempts $max_attempts: not a number"
+if (( max_attempts == 0 )); then
+  echo "The --max-attempts argument should be >0"
+  exit 1
+fi
+grep -q -E '^[0-9]+$' <<<"$wait_seconds" || usage "--wait-seconds $wait_seconds: not a number"
+grep -q -E '^https?://' <<<"$solr_url" || usage "--solr-url $solr_url: not a URL"
+
+((attempts_left=max_attempts))
+while (( attempts_left > 0 )); do
+  if wget -q -O - "$solr_url" | grep -i solr >/dev/null; then
+    break
+  fi
+  (( attempts_left-- ))
+  if (( attempts_left == 0 )); then
+    echo "Solr is still not running; giving up"
+    exit 1
+  fi
+  if (( attempts_left == 1 )); then
+    attempts=attempt
+  else
+    attempts=attempts
+  fi
+  echo "Solr is not running yet on $solr_url. $attempts_left $attempts left"
+  sleep "$wait_seconds"
+done
+echo "Solr is running on $solr_url"
diff --git a/8.11/scripts/wait-for-zookeeper.sh b/8.11/scripts/wait-for-zookeeper.sh
new file mode 100755
index 0000000..82366a4
--- /dev/null
+++ b/8.11/scripts/wait-for-zookeeper.sh
@@ -0,0 +1,165 @@
+#!/bin/bash
+#
+# A helper script to wait for ZooKeeper
+#
+# This script waits for a ZooKeeper master to appear.
+# It repeatedly looks up the name passed as argument
+# in the DNS using getent, and then connects to the
+# ZooKeeper admin port and uses the 'srvr' command to
+# obtain the server's status.
+# You can use this in a Kubernetes init container to
+# delay Solr pods starting until the ZooKeeper service
+# has settled down. Or you could explicitly run this in
+# the Solr container before exec'ing Solr.
+#
+# Inspired by https://github.com/helm/charts/blob/9eba7b1c80990233a68dce48f4a8fe0baf9b7fa5/incubator/solr/templates/statefulset.yaml#L60
+#
+# Usage: wait-for-zookeeper.sh [--max-attempts count] [--wait-seconds seconds] zookeeper-service-name
+#
+# If no argument is provided, but a Solr-style ZK_HOST is set,
+# that will be used. If neither is provided, the default
+# name is 'solr-zookeeper-headless', to match the helm chart.
+
+set -euo pipefail
+
+SCRIPT="$0"
+
+if [[ "${VERBOSE:-}" == "yes" ]]; then
+    set -x
+fi
+
+function usage {
+  echo "$1"
+  echo "Usage: $SCRIPT [--max-attempts count] [--wait-seconds seconds ] zookeeper-service-name"
+  exit 1
+}
+
+TMP_HOSTS="/tmp/hosts.$$"
+TMP_STATUS="/tmp/status.$$"
+
+function cleanup {
+    rm -f $TMP_HOSTS $TMP_STATUS
+}
+
+trap cleanup EXIT
+
+function check_zookeeper {
+    local host=$1
+    local port="${2:-2181}"
+    if ! echo srvr | nc "$host" "$port" > $TMP_STATUS; then
+        echo "Failed to get status from $host"
+        return
+    fi
+    if [ ! -s $TMP_STATUS ]; then
+        echo "No data from $ip"
+        return
+    fi
+    if grep -q 'not currently serving requests' $TMP_STATUS; then
+        echo "Node $ip is not currently serving requests"
+        return
+    fi
+    mode=$(grep "Mode: " $TMP_STATUS | sed 's/Mode: //');
+    if [ -z "$mode" ]; then
+        echo "Cannot determine mode from:"
+        cat $TMP_STATUS
+        return
+    fi
+    echo "Node $ip is a $mode"
+    if [ "$mode" = "leader" ] || [ "$mode" = "standalone" ]; then
+        echo "Done"
+        exit 0
+    fi
+}
+
+max_attempts=120
+wait_seconds=2
+while (( $# > 0 )); do
+  case "$1" in
+   --help)
+     cat <<EOM
+Usage: $SCRIPT [options] zookeeper-service-name
+
+Options:
+  --max-attempts count: number of attempts to check Solr is up. Default: $max_attempts
+  --wait-seconds seconds: number of seconds to wait between attempts. Default: $wait_seconds
+EOM
+     exit 0
+     ;;
+
+   --max-attempts)
+     max_attempts="$2";
+     shift 2;
+     ;;
+
+   --wait-seconds)
+     wait_seconds="$2";
+     shift 2;
+     ;;
+
+   *)
+    if [ -n "${lookup_arg:-}" ]; then
+      usage "Cannot specify multiple zookeeper service names"
+    fi
+    lookup_arg=$1;
+    shift;
+    break;
+    ;;
+
+  esac
+done
+
+grep -q -E '^[0-9]+$' <<<"$max_attempts" || usage "--max-attempts $max_attempts: not a number"
+if (( max_attempts == 0 )); then
+  echo "The --max-attempts argument should be >0"
+  exit 1
+fi
+grep -q -E '^[0-9]+$' <<<"$wait_seconds" || usage "--wait-seconds $wait_seconds: not a number"
+
+if [ -z "${lookup_arg:-}" ]; then
+  if [ -n "$ZK_HOST" ]; then
+    lookup_arg="$ZK_HOST"
+  else
+    lookup_arg=solr-zookeeper-headless
+  fi
+fi
+
+echo "Looking up '$lookup_arg'"
+# split on commas, for when a ZK_HOST string like zoo1:2181,zoo2:2181 is used
+IFS=',' read -ra lookups <<< "$lookup_arg"
+((attempts_left=max_attempts))
+while (( attempts_left > 0 )); do
+  for lookup in "${lookups[@]}"; do
+    if grep -q -E "^\[[0-9].*\]" <<<"$lookup"; then
+      # looks like an IPv6 address, eg [2001:DB8::1] or [2001:DB8::1]:2181
+      # getent does not support the bracket notation, but does support IPv6 addresses
+      host=$(sed -E 's/\[(.*)\].*/\1/' <<<"$lookup")
+      port=$(sed -E 's/^\[(.*)\]:?//' <<<"$lookup")
+    else
+      # IPv4, just split on :
+      IFS=: read -ra split <<<"$lookup"
+      host="${split[0]}"
+      port="${split[1]:-}"
+    fi
+    if [[ "${VERBOSE:-}" == "yes" ]]; then
+      echo "Parsed host=$host port=${port:-}"
+    fi
+    if getent hosts "$host" > $TMP_HOSTS; then
+      while read -r ip hostname ; do
+        echo "${hostname:-}">/dev/null # consume for shellcheck
+        check_zookeeper "$ip" "$port"
+      done <$TMP_HOSTS
+    else
+      echo "Cannot find $lookup yet"
+    fi
+  done
+  (( attempts_left-- ))
+  if (( attempts_left == 0 )); then echo "Still no master found; giving up"
+    exit 1
+  fi
+  sleep "$wait_seconds"
+done
+
+# To test the parsing:
+#  bash scripts/wait-for-zookeeper.sh foo
+#  bash scripts/wait-for-zookeeper.sh 'ZK_HOST=[2001:DB8::1]:2181,[2001:DB8::1],127.0.0.1:2181,127.0.0.2'
+#  ZK_HOST=[2001:DB8::1]:2181,[2001:DB8::1],127.0.0.1:2181,127.0.0.2 bash scripts/wait-for-zookeeper.sh
diff --git a/generate-stackbrew-library.sh b/generate-stackbrew-library.sh
index d025caa..32df8cb 100755
--- a/generate-stackbrew-library.sh
+++ b/generate-stackbrew-library.sh
@@ -74,8 +74,9 @@ GitFetch: refs/heads/main
 EOH
 
 for version in "${versions[@]}"; do
-	for variant in ''; do
-		dir="$version${variant:+/$variant}"
+  # We can remove the variant loop when 8.11 is no longer supported
+	for variant in '' slim; do
+		dir="$version${variant:+-$variant}"
 		[ -f "$dir/Dockerfile" ] || continue
 
 		commit="$(dirCommit "$dir")"
@@ -120,5 +121,3 @@ for version in "${versions[@]}"; do
 		EOE
 	done
 done
-
-cat old-solr-versions