Container Internals and Wrapper Scripts

Jekyll Docker uses a series of integrated shell and Ruby wrapper scripts located in /usr/jekyll/bin/ to manage file permissions, system packages, and command line arguments automatically. This section describes how these wrappers work under the hood.


1. Entrypoint Wrapper (/usr/jekyll/bin/entrypoint)

This is the initial wrapper script executed by the container runtime. It ensures the environment permissions match your host configuration before starting any Ruby processes:

# Check for rootless configuration
if [ "$JEKYLL_ROOTLESS" ]; then
  JEKYLL_UID=0
  JEKYLL_GID=0
fi

: ${JEKYLL_UID:=$(id -u jekyll)}
: ${JEKYLL_GID:=$(id -g jekyll)}
export JEKYLL_UID
export JEKYLL_GID

Key Operations

  • Identity Matching: If the provided JEKYLL_UID differs from the container's default jekyll user account (UID 1000), the script runs usermod and groupmod to update the container's user and group definitions dynamically.
  • Permissions Fixes: Runs a targeted chown over /srv/jekyll and /var/jekyll directories to align workspace access rights and prevent permissions lockouts on your host system.

2. Argument Translation (/usr/jekyll/bin/default-args)

This custom Ruby utility parses and cleans incoming command line arguments, converting environment variables into standard Jekyll CLI flags:

def build?; %w(b build).include?(ARGV[0]); end
def serve?; %w(s serve server).include?(ARGV[0]); end

Key Operations

  • Host Binding: If you run jekyll serve, the script automatically injects -H 0.0.0.0 to bind the server to all network interfaces, allowing you to access the site from your host browser.
  • Flag Injection: Translates environment configurations into standard flags:
  • Appends --force_polling if FORCE_POLLING is active.
  • Appends --drafts if DRAFTS is active.
  • Appends --verbose if VERBOSE is active.
  • Shell Integration: Uses Shellwords.shelljoin to return the cleaned argument array securely back to the execution wrapper.

3. Bundler Override (/usr/jekyll/bin/bundle)

This script replaces the standard bundle executable. It runs pre-flight system package validations and manages local caching directories:

if [ "$1" = "install" ] || [ "$1" = "update" ]; then
  if [ "$1" = "update" ] || ! su-exec jekyll $exe check 1>/dev/null 2>&1; then
    if [ ! -f "/updated" ] && connected && [ -f ".apk" ]; then
      apk add --no-cache --no-progress $(cat .apk)
    fi
    # ... (runs Bundler optimizations)
  fi
fi

Key Operations

  • Automatic Package Installation: If a .apk file is present in your project root, the wrapper reads it and runs apk add to install the packages before starting your Ruby dependencies build.
  • Performance Optimization: Configures standard Bundler optimizations to speed up gem installations:
  • Sets parallel installation jobs (jobs 2).
  • Suppresses unnecessary messages (ignore_messages true).
  • Instructs compiled libraries to use system packages (build.nokogiri --use-system-libraries).

4. Connectivity Engine (/usr/jekyll/bin/connected)

To prevent build delays when working offline, this script checks if the container has internet access before attempting package or gem updates:

url=https://detectportal.firefox.com
if wget -q --spider "$url" -O /dev/null 2>/dev/null; then
  su-exec jekyll touch "$connected"
  exit 0
else
  su-exec jekyll touch $disconnected
  exit 1
fi

Key Operations

  • State Caching: Saves connectivity status files (connected or disconnected) in /var/jekyll/. Subsquent runs check these files first to avoid network timeout delays during offline development.