#!/bin/bash set -euo pipefail # APISIX runtime setup for Cloudron: # 1. persist the Admin API key (rotating it would break automation clients) # 2. relocate the APISIX runtime tree to /app/data - Cloudron mounts the # container rootfs READ-ONLY (writable: /app/data, /tmp, /run), so the # generated config.yaml, nginx.conf, logs and sockets must live there # 3. start the embedded single-node etcd (state under /app/data/etcd, # survives restarts and backups) # 4. regenerate config.yaml on EVERY start, then apisix init + init_etcd # 5. seed the /healthz route once (cosmetic - Cloudron accepts 4xx) # 6. exec openresty in the foreground with the /app/data prefix # # The Admin API listens on 127.0.0.1:9180 ONLY and is never published; # reach it via `cloudron exec` with the key in /app/data/.admin_key. DATA_DIR="/app/data" ETCD_DIR="${DATA_DIR}/etcd" ADMIN_KEY_FILE="${DATA_DIR}/.admin_key" ETCD_ENDPOINT="http://127.0.0.1:2379" PROXY_PORT="${CLOUDRON_APP_PORT:-9080}" # writable runtime tree (container rootfs is read-only on Cloudron). # The apisix CLI derives its install dir from the script path and writes # nginx.conf there, so the lua CLI tree + deps are copied to /app/data and # invoked from the copy (~62 MB, once per start). export APISIX_PREFIX="${DATA_DIR}/apisix" CONF="${APISIX_PREFIX}/conf/config.yaml" mkdir -p "${APISIX_PREFIX}/conf" "${APISIX_PREFIX}/logs" # re-copy on every start so upgrades never run a stale tree rm -rf "${APISIX_PREFIX}/apisix" "${APISIX_PREFIX}/deps" cp -a /usr/local/apisix/apisix "${APISIX_PREFIX}/" cp -a /usr/local/apisix/deps "${APISIX_PREFIX}/" # the CLI hardcodes apisix_home = /usr/local/apisix (read-only); repoint the # copy at the writable prefix so nginx.conf and deps resolve under /app/data grep -rl '/usr/local/apisix' "${APISIX_PREFIX}/apisix" \ | xargs sed -i "s|/usr/local/apisix|${APISIX_PREFIX}|g" # the CLI looks for its config templates under $APISIX_PREFIX/conf cp -a /usr/local/apisix/conf/. "${APISIX_PREFIX}/conf/" APISIX_CLI="/usr/local/openresty/luajit/bin/luajit ${APISIX_PREFIX}/apisix/cli/apisix.lua" # --- 1. persistent Admin API key --------------------------------------------- if [[ ! -s "${ADMIN_KEY_FILE}" ]]; then ( umask 077; head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n' > "${ADMIN_KEY_FILE}" ) echo "Generated new Admin API key at ${ADMIN_KEY_FILE}" fi ADMIN_KEY="$(cat "${ADMIN_KEY_FILE}")" # --- 2. embedded etcd ---------------------------------------------------------- mkdir -p "${ETCD_DIR}" # --initial-cluster is ignored on restarts from an existing data-dir. etcd \ --name default \ --data-dir "${ETCD_DIR}" \ --listen-client-urls "http://127.0.0.1:2379" \ --advertise-client-urls "http://127.0.0.1:2379" \ --listen-peer-urls "http://127.0.0.1:2380" \ --initial-advertise-peer-urls "http://127.0.0.1:2380" \ --initial-cluster "default=http://127.0.0.1:2380" \ --auto-compaction-retention=1h & echo "Waiting for embedded etcd at ${ETCD_ENDPOINT} ..." for i in $(seq 1 30); do if etcdctl --endpoints="${ETCD_ENDPOINT}" endpoint health >/dev/null 2>&1; then echo "etcd is up" break fi if [[ "$i" -eq 30 ]]; then echo "etcd failed to become healthy" >&2 exit 1 fi sleep 1 done # --- 3. generated config (rewritten on every start) ----------------------------- cat > "${CONF}" </healthz a friendly answer instead of a bare 404. if [[ -z "$(etcdctl --endpoints="${ETCD_ENDPOINT}" get /apisix/routes/healthz --print-value-only 2>/dev/null)" ]]; then # ngx.say is unavailable in the rewrite phase - a bare ngx.exit(200) is the # error-proof direct response (200, empty body). etcdctl --endpoints="${ETCD_ENDPOINT}" put /apisix/routes/healthz \ '{"uri":"/healthz","id":"healthz","priority":100,"plugins":{"serverless-pre-function":{"phase":"rewrite","functions":["return function() ngx.exit(200) end"]}}}' > /dev/null \ || echo "WARNING: seeding /healthz failed (cosmetic only, continuing)" >&2 echo "Seeded /healthz route" fi # --- 6. run (foreground) ---------------------------------------------------------- echo "Starting APISIX ..." rm -f "${APISIX_PREFIX}/conf/config_listen.sock" "${APISIX_PREFIX}/logs/worker_events.sock" exec /usr/local/openresty/bin/openresty -p "${APISIX_PREFIX}" -g 'daemon off;'