Docker and Docker-AIO compose setups for SQLite, PocketBase and PostgreSQL
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (windows-latest) (push) Canceled after 0s
CI / Storage tests on PostgreSQL (push) Canceled after 0s
CI / Complement (federation off) (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
CI / Smoke test the binary (push) Canceled after 0s
CI / Admin panel (push) Canceled after 0s
CI / Web client (push) Canceled after 0s
CI / Desktop client (ubuntu-24.04) (push) Canceled after 0s
CI / Desktop client (windows-latest) (push) Canceled after 0s

Docker/ runs the homeserver, admin panel and web client as separate
containers built from the existing Dockerfiles; Docker-AIO/ runs all
three (and optionally PostgreSQL) in one warp-aio image, supervised by
a small entrypoint. Each has a build file and a .prod.yml that only
pulls images from WARP_REGISTRY at WARP_VERSION.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
tajniak81andClaude Opus 5.5 committed 2026-10-19 16:52:10 +02:00
1 parent 829b236efe
commit 6236f5d9a8
17 files changed
+1068

No files matched your search

+1
View File
@@ -13,3 +13,4 @@ Docker-AIO
*.db
*.db-shm
*.db-wal
!Docker-AIO/entrypoint.sh
+111
View File
@@ -0,0 +1,111 @@
# warp-aio — the homeserver, the admin panel and the web client in one
# image, with PostgreSQL bundled for those who want it inside too.
#
# Build from the repository root:
#
# docker build -t warp-aio -f Docker-AIO/Dockerfile .
# docker run -p 8008:8008 -p 8009:8009 -p 8080:8080 -v warp-data:/data \
# -e WARP_SERVER_NAME=warp.example warp-aio
#
# The database is picked the same way as for the homeserver alone, with
# WARP_DB_DRIVER (sqlite, the default, pocketbase or postgres).
# WARP_AIO_EMBEDDED_POSTGRES=true starts the bundled PostgreSQL in the
# container, its data in /data/postgres; see Docker-AIO/entrypoint.sh.
FROM node:22-alpine AS web
WORKDIR /src
RUN corepack enable
# Lockfile and manifests first, so the package store survives source changes.
COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
COPY apps/ui/package.json ./apps/ui/
COPY apps/admin/package.json ./apps/admin/
COPY apps/web/package.json ./apps/web/
RUN pnpm install --frozen-lockfile --filter @warp/admin... --filter @warp/web...
COPY apps/ui ./apps/ui
COPY apps/admin ./apps/admin
COPY apps/web ./apps/web
RUN pnpm --filter @warp/admin build && pnpm --filter @warp/web build
FROM golang:1.26-alpine AS build
ARG VERSION=0.0.0-dev
ARG COMMIT=unknown
ARG BUILD_DATE=unknown
ENV CGO_ENABLED=0
WORKDIR /src
# The homeserver. Dependencies first, so the module cache survives source
# changes.
COPY server/go.mod server/go.sum ./server/
RUN cd server && go mod download
COPY server ./server
RUN cd server && go build -trimpath \
-ldflags "-s -w \
-X github.com/dariuszjaniak/warp/server/internal/buildinfo.Version=${VERSION} \
-X github.com/dariuszjaniak/warp/server/internal/buildinfo.Commit=${COMMIT} \
-X github.com/dariuszjaniak/warp/server/internal/buildinfo.BuildDate=${BUILD_DATE}" \
-o /out/warp ./cmd/warp
# The admin panel and the web client, each with its frontend embedded.
COPY apps/admin/go.mod ./apps/admin/
COPY apps/admin/cmd ./apps/admin/cmd
COPY apps/admin/internal ./apps/admin/internal
COPY --from=web /src/apps/admin/internal/panel/build/dist ./apps/admin/internal/panel/build/dist
RUN cd apps/admin && GOWORK=off go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" \
-o /out/warp-admin ./cmd/warp-admin
COPY apps/web/go.mod ./apps/web/
COPY apps/web/cmd ./apps/web/cmd
COPY apps/web/host ./apps/web/host
COPY apps/web/client/client.go ./apps/web/client/
COPY --from=web /src/apps/web/client/build/dist ./apps/web/client/build/dist
RUN cd apps/web && GOWORK=off go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" \
-o /out/warp-web ./cmd/warp-web
FROM alpine:3.21
# tini is PID 1 so signals reach the entrypoint and zombies are reaped.
# PostgreSQL runs as the same unprivileged user as everything else.
RUN apk add --no-cache ca-certificates tzdata curl tini postgresql17 postgresql17-client && \
adduser -D -u 10001 -h /data warp && \
mkdir -p /run/postgresql && chown warp:warp /run/postgresql
COPY --from=build /out/warp /out/warp-admin /out/warp-web /usr/local/bin/
COPY warp.yaml.example /etc/warp/warp.yaml.example
COPY --chmod=755 Docker-AIO/entrypoint.sh /usr/local/bin/warp-aio
# Database, signing key and media live here; mount a volume on it.
WORKDIR /data
RUN chown warp:warp /data
USER warp
ENV WARP_LISTEN_ADDRESS=":8008" \
WARP_PRIVATE_KEY="/data/signing.key" \
WARP_DB_DRIVER="sqlite" \
WARP_DB_CONNECTION_STRING="file:/data/warp.db?_pragma=journal_mode(WAL)&_pragma=busy_timeout(10000)&_pragma=foreign_keys(1)" \
WARP_MEDIA_BASE_PATH="/data/media" \
WARP_LOG_FORMAT="json" \
WARP_ADMIN_LISTEN=":8009" \
WARP_ADMIN_HOMESERVER="http://127.0.0.1:8008" \
WARP_ADMIN_LOG_FORMAT="json" \
WARP_WEB_LISTEN=":8080" \
WARP_WEB_HOMESERVER="http://127.0.0.1:8008" \
WARP_WEB_LOG_FORMAT="json" \
WARP_AIO_ADMIN="true" \
WARP_AIO_WEB="true" \
WARP_AIO_EMBEDDED_POSTGRES="false"
EXPOSE 8008 8009 8080
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD curl -fsS http://127.0.0.1:8008/healthz && \
{ [ "$WARP_AIO_ADMIN" != true ] || curl -fsS http://127.0.0.1:8009/healthz; } && \
{ [ "$WARP_AIO_WEB" != true ] || curl -fsS http://127.0.0.1:8080/healthz; } || exit 1
ENTRYPOINT ["/sbin/tini", "--", "warp-aio"]
+47
View File
@@ -0,0 +1,47 @@
# Docker-AIO: everything in one image
`warp-aio` holds the homeserver, the admin panel, the web client and
PostgreSQL 17 in one image, built from [`Dockerfile`](Dockerfile).
[`entrypoint.sh`](entrypoint.sh) starts them in one container; if one
exits, the rest are stopped and the container's restart policy brings the
set back.
| File | Database | Image |
| --- | --- | --- |
| `docker-compose.sqlite.yml` | SQLite, `/data/warp.db` | built here |
| `docker-compose.pocketbase.yml` | PocketBase (embedded driver), `/data/pb_data` | built here |
| `docker-compose.postgresql.yml` | PostgreSQL in the container, `/data/postgres` | built here |
| `docker-compose.*.prod.yml` | as above | pulled, never built |
Everything lives in the `warp-data` volume, mounted on `/data`.
```sh
cd Docker-AIO
WARP_SERVER_NAME=matrix.example.org docker compose -f docker-compose.sqlite.yml up -d --build
docker compose -f docker-compose.sqlite.yml exec warp warp create-account --username admin --admin
```
Homeserver on `:8008`, admin panel on `:8009`, web client on `:8080`.
The embedded PostgreSQL listens only on a Unix socket inside the
container, so it needs no password and is not reachable from outside.
## Switches
| Variable | Default | |
| --- | --- | --- |
| `WARP_AIO_ADMIN` | `true` | `false` runs no admin panel |
| `WARP_AIO_WEB` | `true` | `false` runs no web client |
| `WARP_AIO_EMBEDDED_POSTGRES` | `false` | `true` starts the bundled PostgreSQL |
The other settings are those of [Docker/](../Docker/README.md).
## Prod files
The `.prod.yml` files pull `$WARP_REGISTRY/warp-aio:$WARP_VERSION`
(defaults `ghcr.io/dariuszjaniak` and `latest`). Push the image first:
```sh
docker build -t $WARP_REGISTRY/warp-aio:$WARP_VERSION -f Docker-AIO/Dockerfile .
docker push $WARP_REGISTRY/warp-aio:$WARP_VERSION
```
@@ -0,0 +1,54 @@
# Warp all-in-one on PocketBase: the homeserver, the admin panel and the
# web client in one container, pulled from a registry.
#
# cd Docker-AIO
# WARP_SERVER_NAME=matrix.example.org docker compose -f docker-compose.pocketbase.prod.yml up -d
#
# PocketBase is embedded in the homeserver (the pocketbase database driver):
# its data directory, with media in its storage, is /data/pb_data in the
# warp-data volume. Set the WARP_PB_S3_* variables to keep media in an S3
# bucket instead (docs/configuration.md).
#
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
# client http://localhost:8080.
#
# Create the first administrator once the server is up:
#
# docker compose -f docker-compose.pocketbase.prod.yml exec warp warp create-account --username admin --admin
#
# Nothing is built here: the image comes from WARP_REGISTRY (default
# ghcr.io/dariuszjaniak) at tag WARP_VERSION (default latest). Pin
# WARP_VERSION to a release in production, and update with
#
# docker compose -f docker-compose.pocketbase.prod.yml pull && docker compose -f docker-compose.pocketbase.prod.yml up -d
name: warp-aio-pocketbase
services:
warp:
image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp-aio:${WARP_VERSION:-latest}
pull_policy: always
restart: unless-stopped
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
WARP_WELL_KNOWN_CLIENT_BASE_URL: ${WARP_PUBLIC_BASEURL:-http://localhost:8008}
WARP_DB_DRIVER: pocketbase
WARP_DB_CONNECTION_STRING: /data/pb_data
WARP_PB_S3_ENABLED: ${WARP_PB_S3_ENABLED:-false}
WARP_PB_S3_BUCKET: ${WARP_PB_S3_BUCKET:-}
WARP_PB_S3_REGION: ${WARP_PB_S3_REGION:-}
WARP_PB_S3_ENDPOINT: ${WARP_PB_S3_ENDPOINT:-}
WARP_PB_S3_ACCESS_KEY: ${WARP_PB_S3_ACCESS_KEY:-}
WARP_PB_S3_SECRET: ${WARP_PB_S3_SECRET:-}
WARP_PB_S3_FORCE_PATH_STYLE: ${WARP_PB_S3_FORCE_PATH_STYLE:-false}
WARP_REGISTRATION_ENABLED: ${WARP_REGISTRATION_ENABLED:-false}
WARP_FEDERATION_ENABLED: ${WARP_FEDERATION_ENABLED:-false}
volumes:
- warp-data:/data
ports:
- "${WARP_PORT:-8008}:8008"
- "${WARP_ADMIN_PORT:-8009}:8009"
- "${WARP_WEB_PORT:-8080}:8080"
volumes:
warp-data:
+50
View File
@@ -0,0 +1,50 @@
# Warp all-in-one on PocketBase: the homeserver, the admin panel and the
# web client in one container, built from this checkout.
#
# cd Docker-AIO
# WARP_SERVER_NAME=matrix.example.org docker compose -f docker-compose.pocketbase.yml up -d --build
#
# PocketBase is embedded in the homeserver (the pocketbase database driver):
# its data directory, with media in its storage, is /data/pb_data in the
# warp-data volume. Set the WARP_PB_S3_* variables to keep media in an S3
# bucket instead (docs/configuration.md).
#
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
# client http://localhost:8080.
#
# Create the first administrator once the server is up:
#
# docker compose -f docker-compose.pocketbase.yml exec warp warp create-account --username admin --admin
name: warp-aio-pocketbase
services:
warp:
build:
context: ..
dockerfile: Docker-AIO/Dockerfile
image: warp-aio:latest
restart: unless-stopped
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
WARP_WELL_KNOWN_CLIENT_BASE_URL: ${WARP_PUBLIC_BASEURL:-http://localhost:8008}
WARP_DB_DRIVER: pocketbase
WARP_DB_CONNECTION_STRING: /data/pb_data
WARP_PB_S3_ENABLED: ${WARP_PB_S3_ENABLED:-false}
WARP_PB_S3_BUCKET: ${WARP_PB_S3_BUCKET:-}
WARP_PB_S3_REGION: ${WARP_PB_S3_REGION:-}
WARP_PB_S3_ENDPOINT: ${WARP_PB_S3_ENDPOINT:-}
WARP_PB_S3_ACCESS_KEY: ${WARP_PB_S3_ACCESS_KEY:-}
WARP_PB_S3_SECRET: ${WARP_PB_S3_SECRET:-}
WARP_PB_S3_FORCE_PATH_STYLE: ${WARP_PB_S3_FORCE_PATH_STYLE:-false}
WARP_REGISTRATION_ENABLED: ${WARP_REGISTRATION_ENABLED:-false}
WARP_FEDERATION_ENABLED: ${WARP_FEDERATION_ENABLED:-false}
volumes:
- warp-data:/data
ports:
- "${WARP_PORT:-8008}:8008"
- "${WARP_ADMIN_PORT:-8009}:8009"
- "${WARP_WEB_PORT:-8080}:8080"
volumes:
warp-data:
@@ -0,0 +1,48 @@
# Warp all-in-one on PostgreSQL: the homeserver, the admin panel, the web
# client and PostgreSQL in one container, pulled from a registry.
#
# cd Docker-AIO
# WARP_SERVER_NAME=matrix.example.org docker compose -f docker-compose.postgresql.prod.yml up -d
#
# PostgreSQL runs inside the container, listening only on a Unix socket
# there, with its data in /data/postgres in the warp-data volume.
#
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
# client http://localhost:8080.
#
# Create the first administrator once the server is up:
#
# docker compose -f docker-compose.postgresql.prod.yml exec warp warp create-account --username admin --admin
#
# Nothing is built here: the image comes from WARP_REGISTRY (default
# ghcr.io/dariuszjaniak) at tag WARP_VERSION (default latest). Pin
# WARP_VERSION to a release in production, and update with
#
# docker compose -f docker-compose.postgresql.prod.yml pull && docker compose -f docker-compose.postgresql.prod.yml up -d
name: warp-aio-postgresql
services:
warp:
image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp-aio:${WARP_VERSION:-latest}
pull_policy: always
restart: unless-stopped
# PostgreSQL's fast shutdown runs after the homeserver's; give both time.
stop_grace_period: 30s
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
WARP_WELL_KNOWN_CLIENT_BASE_URL: ${WARP_PUBLIC_BASEURL:-http://localhost:8008}
WARP_AIO_EMBEDDED_POSTGRES: "true"
WARP_DB_DRIVER: postgres
WARP_DB_CONNECTION_STRING: postgres:///warp?host=/run/postgresql&user=warp&sslmode=disable
WARP_REGISTRATION_ENABLED: ${WARP_REGISTRATION_ENABLED:-false}
WARP_FEDERATION_ENABLED: ${WARP_FEDERATION_ENABLED:-false}
volumes:
- warp-data:/data
ports:
- "${WARP_PORT:-8008}:8008"
- "${WARP_ADMIN_PORT:-8009}:8009"
- "${WARP_WEB_PORT:-8080}:8080"
volumes:
warp-data:
+44
View File
@@ -0,0 +1,44 @@
# Warp all-in-one on PostgreSQL: the homeserver, the admin panel, the web
# client and PostgreSQL in one container, built from this checkout.
#
# cd Docker-AIO
# WARP_SERVER_NAME=matrix.example.org docker compose -f docker-compose.postgresql.yml up -d --build
#
# PostgreSQL runs inside the container, listening only on a Unix socket
# there, with its data in /data/postgres in the warp-data volume.
#
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
# client http://localhost:8080.
#
# Create the first administrator once the server is up:
#
# docker compose -f docker-compose.postgresql.yml exec warp warp create-account --username admin --admin
name: warp-aio-postgresql
services:
warp:
build:
context: ..
dockerfile: Docker-AIO/Dockerfile
image: warp-aio:latest
restart: unless-stopped
# PostgreSQL's fast shutdown runs after the homeserver's; give both time.
stop_grace_period: 30s
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
WARP_WELL_KNOWN_CLIENT_BASE_URL: ${WARP_PUBLIC_BASEURL:-http://localhost:8008}
WARP_AIO_EMBEDDED_POSTGRES: "true"
WARP_DB_DRIVER: postgres
WARP_DB_CONNECTION_STRING: postgres:///warp?host=/run/postgresql&user=warp&sslmode=disable
WARP_REGISTRATION_ENABLED: ${WARP_REGISTRATION_ENABLED:-false}
WARP_FEDERATION_ENABLED: ${WARP_FEDERATION_ENABLED:-false}
volumes:
- warp-data:/data
ports:
- "${WARP_PORT:-8008}:8008"
- "${WARP_ADMIN_PORT:-8009}:8009"
- "${WARP_WEB_PORT:-8080}:8080"
volumes:
warp-data:
+42
View File
@@ -0,0 +1,42 @@
# Warp all-in-one on SQLite: the homeserver, the admin panel and the web
# client in one container, pulled from a registry.
#
# cd Docker-AIO
# WARP_SERVER_NAME=matrix.example.org docker compose -f docker-compose.sqlite.prod.yml up -d
#
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
# client http://localhost:8080. The database, signing key and media live
# in the warp-data volume.
#
# Create the first administrator once the server is up:
#
# docker compose -f docker-compose.sqlite.prod.yml exec warp warp create-account --username admin --admin
#
# Nothing is built here: the image comes from WARP_REGISTRY (default
# ghcr.io/dariuszjaniak) at tag WARP_VERSION (default latest). Pin
# WARP_VERSION to a release in production, and update with
#
# docker compose -f docker-compose.sqlite.prod.yml pull && docker compose -f docker-compose.sqlite.prod.yml up -d
name: warp-aio-sqlite
services:
warp:
image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp-aio:${WARP_VERSION:-latest}
pull_policy: always
restart: unless-stopped
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
WARP_WELL_KNOWN_CLIENT_BASE_URL: ${WARP_PUBLIC_BASEURL:-http://localhost:8008}
WARP_DB_DRIVER: sqlite
WARP_REGISTRATION_ENABLED: ${WARP_REGISTRATION_ENABLED:-false}
WARP_FEDERATION_ENABLED: ${WARP_FEDERATION_ENABLED:-false}
volumes:
- warp-data:/data
ports:
- "${WARP_PORT:-8008}:8008"
- "${WARP_ADMIN_PORT:-8009}:8009"
- "${WARP_WEB_PORT:-8080}:8080"
volumes:
warp-data:
+38
View File
@@ -0,0 +1,38 @@
# Warp all-in-one on SQLite: the homeserver, the admin panel and the web
# client in one container, built from this checkout.
#
# cd Docker-AIO
# WARP_SERVER_NAME=matrix.example.org docker compose -f docker-compose.sqlite.yml up -d --build
#
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
# client http://localhost:8080. The database, signing key and media live
# in the warp-data volume.
#
# Create the first administrator once the server is up:
#
# docker compose -f docker-compose.sqlite.yml exec warp warp create-account --username admin --admin
name: warp-aio-sqlite
services:
warp:
build:
context: ..
dockerfile: Docker-AIO/Dockerfile
image: warp-aio:latest
restart: unless-stopped
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
WARP_WELL_KNOWN_CLIENT_BASE_URL: ${WARP_PUBLIC_BASEURL:-http://localhost:8008}
WARP_DB_DRIVER: sqlite
WARP_REGISTRATION_ENABLED: ${WARP_REGISTRATION_ENABLED:-false}
WARP_FEDERATION_ENABLED: ${WARP_FEDERATION_ENABLED:-false}
volumes:
- warp-data:/data
ports:
- "${WARP_PORT:-8008}:8008"
- "${WARP_ADMIN_PORT:-8009}:8009"
- "${WARP_WEB_PORT:-8080}:8080"
volumes:
warp-data:
+104
View File
@@ -0,0 +1,104 @@
#!/bin/sh
# warp-aio: runs the homeserver, the admin panel and the web client (and,
# with WARP_AIO_EMBEDDED_POSTGRES=true, PostgreSQL) in one container.
#
# If any of them exits, the others are stopped and the container exits, so
# the restart policy brings the whole set back. SIGTERM stops the clients
# first, then the homeserver, then PostgreSQL.
#
# 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); point
# WARP_DB_CONNECTION_STRING at
# postgres:///warp?host=/run/postgresql&user=warp&sslmode=disable
set -eu
if [ "$#" -gt 0 ]; then
exec "$@"
fi
log() { echo "warp-aio: $*" >&2; }
pg_pid=""
warp_pid=""
admin_pid=""
web_pid=""
# stop PID...: SIGTERM, then wait for each to exit.
stop() {
for pid in "$@"; do
[ -n "$pid" ] && kill -TERM "$pid" 2>/dev/null || true
done
for pid in "$@"; do
[ -n "$pid" ] && wait "$pid" 2>/dev/null || true
done
}
shutdown() {
trap '' TERM INT
stop "$web_pid" "$admin_pid"
stop "$warp_pid"
if [ -n "$pg_pid" ]; then
# SIGINT is PostgreSQL's fast shutdown: it does not wait for
# clients, and the homeserver is gone by now anyway.
kill -INT "$pg_pid" 2>/dev/null || true
wait "$pg_pid" 2>/dev/null || true
fi
exit "${1:-0}"
}
trap 'log "stopping"; shutdown 0' TERM INT
if [ "$WARP_AIO_EMBEDDED_POSTGRES" = true ]; then
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
postgres -D "$PGDATA" -c listen_addresses='' -c unix_socket_directories=/run/postgresql &
pg_pid=$!
until pg_isready -q -h /run/postgresql -U warp; do
kill -0 "$pg_pid" 2>/dev/null || { log "PostgreSQL failed to start"; exit 1; }
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
# 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
warp serve &
warp_pid=$!
if [ "$WARP_AIO_ADMIN" = true ]; then
warp-admin &
admin_pid=$!
fi
if [ "$WARP_AIO_WEB" = true ]; then
warp-web &
web_pid=$!
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
for entry in "postgres:$pg_pid" "warp:$warp_pid" "warp-admin:$admin_pid" "warp-web:$web_pid"; do
pid="${entry#*:}"
if [ -n "$pid" ] && ! kill -0 "$pid" 2>/dev/null; then
log "${entry%%:*} exited; stopping the rest"
shutdown 1
fi
done
sleep 1 &
wait $! || true
done
+57
View File
@@ -0,0 +1,57 @@
# Docker: one container per component
The homeserver (`warp`), the admin panel (`warp-admin`) and the web client
(`warp-web`) as separate images, built from `Dockerfile`,
`apps/admin/Dockerfile` and `apps/web/Dockerfile`.
| File | Database | Images |
| --- | --- | --- |
| `docker-compose.sqlite.yml` | SQLite, in the `warp-data` volume | built here |
| `docker-compose.pocketbase.yml` | PocketBase, embedded in the homeserver, in `warp-data` | built here |
| `docker-compose.postgresql.yml` | PostgreSQL 17, its own container | built here |
| `docker-compose.*.prod.yml` | as above | pulled, never built |
```sh
cd Docker
WARP_SERVER_NAME=matrix.example.org docker compose -f docker-compose.sqlite.yml up -d --build
docker compose -f docker-compose.sqlite.yml exec warp warp create-account --username admin --admin
```
Homeserver on `:8008`, admin panel on `:8009`, web client on `:8080`
(`WARP_PORT`, `WARP_ADMIN_PORT`, `WARP_WEB_PORT` move them). Put a
TLS-terminating reverse proxy in front before exposing them; see
[docs/deployment.md](../docs/deployment.md).
PocketBase is a database driver inside the homeserver, not a separate
server, so the PocketBase files have no PocketBase container.
## Prod files
The `.prod.yml` files pull `$WARP_REGISTRY/warp`, `warp-admin` and
`warp-web` at tag `$WARP_VERSION` (defaults `ghcr.io/dariuszjaniak` and
`latest`). Push the images there first:
```sh
docker compose -f docker-compose.sqlite.yml build
for i in warp warp-admin warp-web; do
docker tag $i:latest $WARP_REGISTRY/$i:$WARP_VERSION
docker push $WARP_REGISTRY/$i:$WARP_VERSION
done
```
`docker-compose.postgresql.prod.yml` refuses to start without
`WARP_POSTGRES_PASSWORD`.
## Settings
| Variable | Default |
| --- | --- |
| `WARP_SERVER_NAME` | `localhost` |
| `WARP_PUBLIC_BASEURL` | `http://localhost:8008` |
| `WARP_REGISTRATION_ENABLED` | `false` |
| `WARP_FEDERATION_ENABLED` | `false` |
| `WARP_POSTGRES_PASSWORD` | `warp` (required in prod) |
| `WARP_PB_S3_*` | S3 media for PocketBase, off |
Any other setting from [docs/configuration.md](../docs/configuration.md)
can be added to the `warp` service's environment.
+79
View File
@@ -0,0 +1,79 @@
# Warp on PocketBase: the homeserver, the admin panel and the web client,
# each in its own container, pulled from a registry.
#
# cd Docker
# WARP_SERVER_NAME=matrix.example.org docker compose -f docker-compose.pocketbase.prod.yml up -d
#
# PocketBase is embedded in the homeserver (the pocketbase database driver),
# so it is not a container of its own: its data directory, with media in
# its storage, is /data/pb_data in the warp-data volume. Set the WARP_PB_S3_*
# variables to keep media in an S3 bucket instead (docs/configuration.md).
#
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
# client http://localhost:8080.
#
# Create the first administrator once the server is up:
#
# docker compose -f docker-compose.pocketbase.prod.yml exec warp warp create-account --username admin --admin
#
# Nothing is built here: the images come from WARP_REGISTRY (default
# ghcr.io/dariuszjaniak) at tag WARP_VERSION (default latest). Pin
# WARP_VERSION to a release in production, and update with
#
# docker compose -f docker-compose.pocketbase.prod.yml pull && docker compose -f docker-compose.pocketbase.prod.yml up -d
name: warp-pocketbase
services:
warp:
image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp:${WARP_VERSION:-latest}
pull_policy: always
restart: unless-stopped
# The signing key is created on first start and kept in the volume:
# losing it means other servers no longer recognise this one.
entrypoint: ["/bin/sh", "-c"]
command:
- test -f /data/signing.key || warp generate-keys --output /data/signing.key; exec warp serve
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
WARP_WELL_KNOWN_CLIENT_BASE_URL: ${WARP_PUBLIC_BASEURL:-http://localhost:8008}
WARP_DB_DRIVER: pocketbase
WARP_DB_CONNECTION_STRING: /data/pb_data
WARP_PB_S3_ENABLED: ${WARP_PB_S3_ENABLED:-false}
WARP_PB_S3_BUCKET: ${WARP_PB_S3_BUCKET:-}
WARP_PB_S3_REGION: ${WARP_PB_S3_REGION:-}
WARP_PB_S3_ENDPOINT: ${WARP_PB_S3_ENDPOINT:-}
WARP_PB_S3_ACCESS_KEY: ${WARP_PB_S3_ACCESS_KEY:-}
WARP_PB_S3_SECRET: ${WARP_PB_S3_SECRET:-}
WARP_PB_S3_FORCE_PATH_STYLE: ${WARP_PB_S3_FORCE_PATH_STYLE:-false}
WARP_REGISTRATION_ENABLED: ${WARP_REGISTRATION_ENABLED:-false}
WARP_FEDERATION_ENABLED: ${WARP_FEDERATION_ENABLED:-false}
volumes:
- warp-data:/data
ports:
- "${WARP_PORT:-8008}:8008"
admin:
image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp-admin:${WARP_VERSION:-latest}
pull_policy: always
restart: unless-stopped
depends_on:
- warp
environment:
WARP_ADMIN_HOMESERVER: http://warp:8008
ports:
- "${WARP_ADMIN_PORT:-8009}:8009"
web:
image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp-web:${WARP_VERSION:-latest}
pull_policy: always
restart: unless-stopped
depends_on:
- warp
environment:
WARP_WEB_HOMESERVER: http://warp:8008
ports:
- "${WARP_WEB_PORT:-8080}:8080"
volumes:
warp-data:
+79
View File
@@ -0,0 +1,79 @@
# Warp on PocketBase: the homeserver, the admin panel and the web client,
# each in its own container, built from this checkout.
#
# cd Docker
# WARP_SERVER_NAME=matrix.example.org docker compose -f docker-compose.pocketbase.yml up -d --build
#
# PocketBase is embedded in the homeserver (the pocketbase database driver),
# so it is not a container of its own: its data directory, with media in
# its storage, is /data/pb_data in the warp-data volume. Set the WARP_PB_S3_*
# variables to keep media in an S3 bucket instead (docs/configuration.md).
#
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
# client http://localhost:8080.
#
# Create the first administrator once the server is up:
#
# docker compose -f docker-compose.pocketbase.yml exec warp warp create-account --username admin --admin
name: warp-pocketbase
services:
warp:
build:
context: ..
dockerfile: Dockerfile
image: warp:latest
restart: unless-stopped
# The signing key is created on first start and kept in the volume:
# losing it means other servers no longer recognise this one.
entrypoint: ["/bin/sh", "-c"]
command:
- test -f /data/signing.key || warp generate-keys --output /data/signing.key; exec warp serve
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
WARP_WELL_KNOWN_CLIENT_BASE_URL: ${WARP_PUBLIC_BASEURL:-http://localhost:8008}
WARP_DB_DRIVER: pocketbase
WARP_DB_CONNECTION_STRING: /data/pb_data
WARP_PB_S3_ENABLED: ${WARP_PB_S3_ENABLED:-false}
WARP_PB_S3_BUCKET: ${WARP_PB_S3_BUCKET:-}
WARP_PB_S3_REGION: ${WARP_PB_S3_REGION:-}
WARP_PB_S3_ENDPOINT: ${WARP_PB_S3_ENDPOINT:-}
WARP_PB_S3_ACCESS_KEY: ${WARP_PB_S3_ACCESS_KEY:-}
WARP_PB_S3_SECRET: ${WARP_PB_S3_SECRET:-}
WARP_PB_S3_FORCE_PATH_STYLE: ${WARP_PB_S3_FORCE_PATH_STYLE:-false}
WARP_REGISTRATION_ENABLED: ${WARP_REGISTRATION_ENABLED:-false}
WARP_FEDERATION_ENABLED: ${WARP_FEDERATION_ENABLED:-false}
volumes:
- warp-data:/data
ports:
- "${WARP_PORT:-8008}:8008"
admin:
build:
context: ..
dockerfile: apps/admin/Dockerfile
image: warp-admin:latest
restart: unless-stopped
depends_on:
- warp
environment:
WARP_ADMIN_HOMESERVER: http://warp:8008
ports:
- "${WARP_ADMIN_PORT:-8009}:8009"
web:
build:
context: ..
dockerfile: apps/web/Dockerfile
image: warp-web:latest
restart: unless-stopped
depends_on:
- warp
environment:
WARP_WEB_HOMESERVER: http://warp:8008
ports:
- "${WARP_WEB_PORT:-8080}:8080"
volumes:
warp-data:
+90
View File
@@ -0,0 +1,90 @@
# Warp on PostgreSQL: the homeserver, the admin panel, the web client and
# PostgreSQL, each in its own container, pulled from a registry.
#
# cd Docker
# WARP_SERVER_NAME=matrix.example.org WARP_POSTGRES_PASSWORD=change-me \
# docker compose -f docker-compose.postgresql.prod.yml up -d
#
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
# client http://localhost:8080. PostgreSQL is reachable only on the compose
# network.
#
# Create the first administrator once the server is up:
#
# docker compose -f docker-compose.postgresql.prod.yml exec warp warp create-account --username admin --admin
#
# Nothing is built here: the images come from WARP_REGISTRY (default
# ghcr.io/dariuszjaniak) at tag WARP_VERSION (default latest). Pin
# WARP_VERSION to a release in production, and update with
#
# docker compose -f docker-compose.postgresql.prod.yml pull && docker compose -f docker-compose.postgresql.prod.yml up -d
name: warp-postgresql
services:
postgres:
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_USER: warp
POSTGRES_PASSWORD: ${WARP_POSTGRES_PASSWORD:?set WARP_POSTGRES_PASSWORD}
POSTGRES_DB: warp
# C sorts by byte, which is what room and event IDs are compared by.
POSTGRES_INITDB_ARGS: --encoding=UTF8 --lc-collate=C --lc-ctype=C
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U warp -d warp"]
interval: 5s
timeout: 5s
retries: 20
warp:
image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp:${WARP_VERSION:-latest}
pull_policy: always
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
# The signing key is created on first start and kept in the volume:
# losing it means other servers no longer recognise this one.
entrypoint: ["/bin/sh", "-c"]
command:
- test -f /data/signing.key || warp generate-keys --output /data/signing.key; exec warp serve
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
WARP_WELL_KNOWN_CLIENT_BASE_URL: ${WARP_PUBLIC_BASEURL:-http://localhost:8008}
WARP_DB_DRIVER: postgres
WARP_DB_CONNECTION_STRING: postgres://warp:${WARP_POSTGRES_PASSWORD:?set WARP_POSTGRES_PASSWORD}@postgres:5432/warp?sslmode=disable
WARP_REGISTRATION_ENABLED: ${WARP_REGISTRATION_ENABLED:-false}
WARP_FEDERATION_ENABLED: ${WARP_FEDERATION_ENABLED:-false}
volumes:
- warp-data:/data
ports:
- "${WARP_PORT:-8008}:8008"
admin:
image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp-admin:${WARP_VERSION:-latest}
pull_policy: always
restart: unless-stopped
depends_on:
- warp
environment:
WARP_ADMIN_HOMESERVER: http://warp:8008
ports:
- "${WARP_ADMIN_PORT:-8009}:8009"
web:
image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp-web:${WARP_VERSION:-latest}
pull_policy: always
restart: unless-stopped
depends_on:
- warp
environment:
WARP_WEB_HOMESERVER: http://warp:8008
ports:
- "${WARP_WEB_PORT:-8080}:8080"
volumes:
postgres-data:
warp-data:
+90
View File
@@ -0,0 +1,90 @@
# Warp on PostgreSQL: the homeserver, the admin panel, the web client and
# PostgreSQL, each in its own container, built from this checkout.
#
# cd Docker
# WARP_SERVER_NAME=matrix.example.org WARP_POSTGRES_PASSWORD=change-me \
# docker compose -f docker-compose.postgresql.yml up -d --build
#
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
# client http://localhost:8080. PostgreSQL is reachable only on the compose
# network.
#
# Create the first administrator once the server is up:
#
# docker compose -f docker-compose.postgresql.yml exec warp warp create-account --username admin --admin
name: warp-postgresql
services:
postgres:
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_USER: warp
POSTGRES_PASSWORD: ${WARP_POSTGRES_PASSWORD:-warp}
POSTGRES_DB: warp
# C sorts by byte, which is what room and event IDs are compared by.
POSTGRES_INITDB_ARGS: --encoding=UTF8 --lc-collate=C --lc-ctype=C
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U warp -d warp"]
interval: 5s
timeout: 5s
retries: 20
warp:
build:
context: ..
dockerfile: Dockerfile
image: warp:latest
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
# The signing key is created on first start and kept in the volume:
# losing it means other servers no longer recognise this one.
entrypoint: ["/bin/sh", "-c"]
command:
- test -f /data/signing.key || warp generate-keys --output /data/signing.key; exec warp serve
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
WARP_WELL_KNOWN_CLIENT_BASE_URL: ${WARP_PUBLIC_BASEURL:-http://localhost:8008}
WARP_DB_DRIVER: postgres
WARP_DB_CONNECTION_STRING: postgres://warp:${WARP_POSTGRES_PASSWORD:-warp}@postgres:5432/warp?sslmode=disable
WARP_REGISTRATION_ENABLED: ${WARP_REGISTRATION_ENABLED:-false}
WARP_FEDERATION_ENABLED: ${WARP_FEDERATION_ENABLED:-false}
volumes:
- warp-data:/data
ports:
- "${WARP_PORT:-8008}:8008"
admin:
build:
context: ..
dockerfile: apps/admin/Dockerfile
image: warp-admin:latest
restart: unless-stopped
depends_on:
- warp
environment:
WARP_ADMIN_HOMESERVER: http://warp:8008
ports:
- "${WARP_ADMIN_PORT:-8009}:8009"
web:
build:
context: ..
dockerfile: apps/web/Dockerfile
image: warp-web:latest
restart: unless-stopped
depends_on:
- warp
environment:
WARP_WEB_HOMESERVER: http://warp:8008
ports:
- "${WARP_WEB_PORT:-8080}:8080"
volumes:
postgres-data:
warp-data:
+67
View File
@@ -0,0 +1,67 @@
# Warp on SQLite: the homeserver, the admin panel and the web client, each
# in its own container, pulled from a registry.
#
# cd Docker
# WARP_SERVER_NAME=matrix.example.org docker compose -f docker-compose.sqlite.prod.yml up -d
#
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
# client http://localhost:8080. The database, signing key and media live
# in the warp-data volume.
#
# Create the first administrator once the server is up:
#
# docker compose -f docker-compose.sqlite.prod.yml exec warp warp create-account --username admin --admin
#
# Nothing is built here: the images come from WARP_REGISTRY (default
# ghcr.io/dariuszjaniak) at tag WARP_VERSION (default latest). Pin
# WARP_VERSION to a release in production, and update with
#
# docker compose -f docker-compose.sqlite.prod.yml pull && docker compose -f docker-compose.sqlite.prod.yml up -d
name: warp-sqlite
services:
warp:
image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp:${WARP_VERSION:-latest}
pull_policy: always
restart: unless-stopped
# The signing key is created on first start and kept in the volume:
# losing it means other servers no longer recognise this one.
entrypoint: ["/bin/sh", "-c"]
command:
- test -f /data/signing.key || warp generate-keys --output /data/signing.key; exec warp serve
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
WARP_WELL_KNOWN_CLIENT_BASE_URL: ${WARP_PUBLIC_BASEURL:-http://localhost:8008}
WARP_DB_DRIVER: sqlite
WARP_REGISTRATION_ENABLED: ${WARP_REGISTRATION_ENABLED:-false}
WARP_FEDERATION_ENABLED: ${WARP_FEDERATION_ENABLED:-false}
volumes:
- warp-data:/data
ports:
- "${WARP_PORT:-8008}:8008"
admin:
image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp-admin:${WARP_VERSION:-latest}
pull_policy: always
restart: unless-stopped
depends_on:
- warp
environment:
WARP_ADMIN_HOMESERVER: http://warp:8008
ports:
- "${WARP_ADMIN_PORT:-8009}:8009"
web:
image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp-web:${WARP_VERSION:-latest}
pull_policy: always
restart: unless-stopped
depends_on:
- warp
environment:
WARP_WEB_HOMESERVER: http://warp:8008
ports:
- "${WARP_WEB_PORT:-8080}:8080"
volumes:
warp-data:
+67
View File
@@ -0,0 +1,67 @@
# Warp on SQLite: the homeserver, the admin panel and the web client, each
# in its own container, built from this checkout.
#
# cd Docker
# WARP_SERVER_NAME=matrix.example.org docker compose -f docker-compose.sqlite.yml up -d --build
#
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
# client http://localhost:8080. The database, signing key and media live
# in the warp-data volume.
#
# Create the first administrator once the server is up:
#
# docker compose -f docker-compose.sqlite.yml exec warp warp create-account --username admin --admin
name: warp-sqlite
services:
warp:
build:
context: ..
dockerfile: Dockerfile
image: warp:latest
restart: unless-stopped
# The signing key is created on first start and kept in the volume:
# losing it means other servers no longer recognise this one.
entrypoint: ["/bin/sh", "-c"]
command:
- test -f /data/signing.key || warp generate-keys --output /data/signing.key; exec warp serve
environment:
WARP_SERVER_NAME: ${WARP_SERVER_NAME:-localhost}
WARP_WELL_KNOWN_CLIENT_BASE_URL: ${WARP_PUBLIC_BASEURL:-http://localhost:8008}
WARP_DB_DRIVER: sqlite
WARP_REGISTRATION_ENABLED: ${WARP_REGISTRATION_ENABLED:-false}
WARP_FEDERATION_ENABLED: ${WARP_FEDERATION_ENABLED:-false}
volumes:
- warp-data:/data
ports:
- "${WARP_PORT:-8008}:8008"
admin:
build:
context: ..
dockerfile: apps/admin/Dockerfile
image: warp-admin:latest
restart: unless-stopped
depends_on:
- warp
environment:
WARP_ADMIN_HOMESERVER: http://warp:8008
ports:
- "${WARP_ADMIN_PORT:-8009}:8009"
web:
build:
context: ..
dockerfile: apps/web/Dockerfile
image: warp-web:latest
restart: unless-stopped
depends_on:
- warp
environment:
WARP_WEB_HOMESERVER: http://warp:8008
ports:
- "${WARP_WEB_PORT:-8080}:8080"
volumes:
warp-data: