Files
Warp/Docker-AIO/entrypoint.sh
T
tajniak81andClaude Opus 5.5 36d83551aa Docker-AIO: PocketBase images with SeaweedFS for media
Dockerfile.PocketBase.SeaweedFS bakes SeaweedFS into the PocketBase
all-in-one image as one `weed server -s3` process;
Dockerfile.PocketBase.SeaweedFS.split runs master, volume, filer, S3
gateway and the admin UI as separate processes, with the homeserver's
S3 identity seeded into the filer's IAM store on every boot. Media goes
to the bucket, the database stays in /data/pb_data, and both layouts
share /seaweed/data, so switching between them needs no migration.

The entrypoint now tracks every process in one list and stops them in
reverse start order, and it refuses to start an open S3 gateway or
admin UI. Each image gets a build and a prod compose file, and
.env.example and the README cover the new settings. Modelled on
DriverVault's Docker-AIO SeaweedFS images.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:33:21 +02:00

203 lines
7.8 KiB
Bash

#!/bin/sh
# warp-aio: runs the homeserver, the admin panel and the web client in one
# container, with whatever the image adds: PostgreSQL, or SeaweedFS as the
# media store.
#
# If any process exits, the others are stopped and the container exits, so
# the restart policy brings the whole set back. SIGTERM stops them in the
# reverse of the order they started: the clients, the homeserver, then
# SeaweedFS and PostgreSQL underneath it.
#
# With arguments, runs them instead: `docker run warp-aio warp version`.
#
# WARP_AIO_ADMIN=false no admin panel
# WARP_AIO_WEB=false no web client
# WARP_AIO_EMBEDDED_POSTGRES=true PostgreSQL in the container, data in
# $PGDATA (/data/postgres); the default
# in warp-aio-postgresql, the only image
# that has PostgreSQL
# WARP_AIO_SEAWEEDFS=server SeaweedFS as one `weed server -s3`
# WARP_AIO_SEAWEEDFS=split SeaweedFS as master, volume, filer, S3
# gateway and admin UI, one process each;
# set by the warp-aio-pocketbase-seaweedfs
# images, the only ones with SeaweedFS
set -eu
if [ "$#" -gt 0 ]; then
exec "$@"
fi
log() { echo "warp-aio: $*" >&2; }
# "name:pid" of every process started, newest first, so shutdown walks it in
# reverse start order.
procs=""
# run NAME COMMAND...: start COMMAND in the background and track it.
run() {
name=$1
shift
"$@" &
procs="$name:$! $procs"
}
shutdown() {
trap '' TERM INT
for entry in $procs; do
name=${entry%%:*}
pid=${entry#*:}
# SIGINT is PostgreSQL's fast shutdown: it does not wait for
# clients, and everything that used it is gone by now.
if [ "$name" = postgres ]; then sig=INT; else sig=TERM; fi
kill -"$sig" "$pid" 2>/dev/null || true
wait "$pid" 2>/dev/null || true
done
exit "${1:-0}"
}
trap 'log "stopping"; shutdown 0' TERM INT
# check: stop everything if any process has exited.
check() {
for entry in $procs; do
if ! kill -0 "${entry#*:}" 2>/dev/null; then
log "${entry%%:*} exited; stopping the rest"
shutdown 1
fi
done
}
# wait_http NAME URL: wait until URL answers, as long as nothing has died.
wait_http() {
until curl -fsS -o /dev/null "$2" 2>/dev/null; do
check
log "waiting for $1"
sleep 1
done
}
if [ "${WARP_AIO_EMBEDDED_POSTGRES:-false}" = true ]; then
if ! command -v postgres >/dev/null; then
log "WARP_AIO_EMBEDDED_POSTGRES=true, but this image has no PostgreSQL; use warp-aio-postgresql"
exit 1
fi
export PGDATA="${PGDATA:-/data/postgres}"
if [ ! -s "$PGDATA/PG_VERSION" ]; then
log "initialising PostgreSQL in $PGDATA"
# Trust is safe here: PostgreSQL listens on no TCP address, only on
# a socket inside this container.
initdb -D "$PGDATA" -U warp --auth=trust --encoding=UTF8 --locale=C >/dev/null
fi
run postgres postgres -D "$PGDATA" -c listen_addresses='' -c unix_socket_directories=/run/postgresql
until pg_isready -q -h /run/postgresql -U warp; do
check
sleep 1
done
if [ "$(psql -h /run/postgresql -U warp -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'warp'")" != 1 ]; then
createdb -h /run/postgresql -U warp warp
fi
fi
# SeaweedFS keeps everything under /seaweed/data, in the layout `weed server
# -dir` writes, which the split roles share as well: switching between the
# two images needs no migration. Every role advertises 127.0.0.1; the volume
# server serves file content by id with no authentication at all, so only
# the S3 gateway and the admin UI are meant to be published. The volume
# server is on 8081 because 8080 is the web client.
seaweedfs="${WARP_AIO_SEAWEEDFS:-}"
if [ -n "$seaweedfs" ]; then
if ! command -v weed >/dev/null; then
log "WARP_AIO_SEAWEEDFS=$seaweedfs, but this image has no SeaweedFS; use warp-aio-pocketbase-seaweedfs"
exit 1
fi
# One pair of credentials: the gateway's identity and what the
# homeserver signs its requests with. An empty pair would leave the
# gateway in its allow-anyone default, so refuse instead.
if [ -z "${WARP_PB_S3_ACCESS_KEY:-}" ] || [ -z "${WARP_PB_S3_SECRET:-}" ]; then
log "WARP_PB_S3_ACCESS_KEY and WARP_PB_S3_SECRET are required; refusing to start an unauthenticated S3 gateway"
exit 1
fi
bucket="${WARP_PB_S3_BUCKET:-warp}"
mkdir -p /seaweed/data
fi
case "$seaweedfs" in
"") ;;
server)
# Master, volume, filer and gateway as goroutines of one process. With
# no -s3.config, the gateway takes its single identity from
# AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY, given to it alone.
run seaweedfs env AWS_ACCESS_KEY_ID="$WARP_PB_S3_ACCESS_KEY" AWS_SECRET_ACCESS_KEY="$WARP_PB_S3_SECRET" \
weed server -dir=/seaweed/data -ip=127.0.0.1 -ip.bind=0.0.0.0 \
-volume.port=8081 -volume.max=0 -master.volumeSizeLimitMB=1024 -s3 -s3.port=8333
wait_http "SeaweedFS S3 gateway" http://127.0.0.1:8333/healthz
# The homeserver never creates its bucket. Creating one that exists is
# a no-op.
echo "s3.bucket.create -name $bucket" | weed shell -master=127.0.0.1:9333 >/dev/null || true
;;
split)
# The admin UI can mint credentials for the bucket, and weed serves it
# with no authentication when the password is empty.
if [ -z "${WEED_ADMIN_PASSWORD:-}" ]; then
log "WEED_ADMIN_PASSWORD is empty; refusing to serve an unauthenticated SeaweedFS admin UI"
exit 1
fi
mkdir -p /seaweed/admin
run seaweedfs-master weed master -ip=127.0.0.1 -ip.bind=127.0.0.1 -port=9333 \
-mdir=/seaweed/data -volumeSizeLimitMB=1024 -metricsPort=9324
wait_http "SeaweedFS master" http://127.0.0.1:9333/cluster/status
run seaweedfs-volume weed volume -master=127.0.0.1:9333 -ip=127.0.0.1 -ip.bind=127.0.0.1 \
-port=8081 -dir=/seaweed/data -max=0 -metricsPort=9325
wait_http "SeaweedFS volume server" http://127.0.0.1:8081/healthz
run seaweedfs-filer weed filer -master=127.0.0.1:9333 -ip=127.0.0.1 -ip.bind=127.0.0.1 \
-port=8888 -defaultStoreDir=/seaweed/data -metricsPort=9326
wait_http "SeaweedFS filer" http://127.0.0.1:8888/healthz
# The bucket, and the homeserver's identity in the filer's IAM store,
# both re-applied on every boot; that is how a rotated secret lands.
# The store is the one the admin UI writes, so the gateway runs with no
# -config and no AWS_* variables: those are lower-priority sources that
# the first identity added in the admin UI would silently displace.
shell() { weed shell -master=127.0.0.1:9333 -filer=127.0.0.1:8888; }
printf '%s\n' \
"s3.bucket.create -name $bucket" \
"s3.configure -user warp -access_key $WARP_PB_S3_ACCESS_KEY -secret_key $WARP_PB_S3_SECRET -actions Admin -apply" |
shell >/dev/null || true
# An empty IAM store would leave the gateway open to anyone.
if ! echo "s3.configure" | shell 2>/dev/null | grep -q "$WARP_PB_S3_ACCESS_KEY"; then
log "the homeserver's S3 identity is not in the filer's IAM store; refusing to start the gateway"
shutdown 1
fi
run seaweedfs-s3 weed s3 -filer=127.0.0.1:8888 -ip.bind=0.0.0.0 -port=8333 -metricsPort=9327
run seaweedfs-admin weed admin -port=23646 -master=127.0.0.1:9333 -dataDir=/seaweed/admin -metricsPort=9328
wait_http "SeaweedFS S3 gateway" http://127.0.0.1:8333/healthz
;;
*)
log "WARP_AIO_SEAWEEDFS must be server, split or empty, not $seaweedfs"
exit 1
;;
esac
# The signing key is created on first start and kept in the volume: losing
# it means other servers no longer recognise this one.
if [ ! -f "$WARP_PRIVATE_KEY" ]; then
warp generate-keys --output "$WARP_PRIVATE_KEY"
fi
run warp warp serve
if [ "${WARP_AIO_ADMIN:-true}" = true ]; then
run warp-admin warp-admin
fi
if [ "${WARP_AIO_WEB:-true}" = true ]; then
run warp-web warp-web
fi
# Busybox sh has no `wait -n`, so check on the processes every second. The
# sleep runs in the background so a signal is handled at once.
while :; do
check
sleep 1 &
wait $! || true
done