Skip to content

Docker ​

The skeleton ships both setups, on Postgres (one database per module) and Redis (the event stream), served by Octane with Swoole:

bash
# every module in one container
docker compose -f docker-compose.mono.yml up -d
# one container per module
docker compose up -d

In both, Traefik is the only way into the API, on :8000 (GATEWAY_PORT); the modules' ports stay private, except Reverb's 8080 (the live push). Postgres and the modules' Redis are on a network Traefik doesn't join; it reads the users from gateway-cache, a Redis of its own. Send the token iam issued: Traefik turns it into the X-Identity the modules read. The container that runs iam writes its JWT key pair to the jwt-keys volume at its first start (WITH_JWT_KEYS), and Traefik reads the public key there.

In the second setup, each image is built with --build-arg RUN_MODULES=<module>, so it holds only its module: the Dockerfile runs modules:purge before composer dump-autoload. A container migrates only the database of the module it runs, so two containers never migrate the same one.

FileWhat it does
docker/DockerfileInstalls the dependencies, purges the modules the image doesn't run and the packages only they required, then builds the PHP and Swoole runtime.
docker/entrypoint.shWrites iam's JWT keys (WITH_JWT_KEYS), runs optimize, migrates (on the http role only), then starts supervisord.
gateway/Traefik in front of the modules, and its auth plugin: see gateway/README.md.
docker/supervisord.confThe roles a container can take, each switched on by a variable.
docker/postgres/A Postgres image that creates the databases of MODULE_DATABASES, owned by distributable, written by distributable_app.
RoleVariableDefaultRun at most
http (Octane)WITH_HTTPtrueas many as you need
rpc (rpc:serve, port DISTRIBUTABLE_RPC_PORT)WITH_RPCfalseas many as you need: the calls between modules, kept off the API's workers
worker (queue:work)WITH_WORKERfalseas many as you need
reverb (WebSocket, port 8080)WITH_REVERBfalseone per container that pushes: it pushes to its own, on localhost
scheduler (supercronic)WITH_SCHEDULERfalseone per module set: two would run each task twice
consumers (stream:consume)WITH_CONSUMERSfalseone per module set: it serves every module of RUN_MODULES, each with its own cursor, and two would break the order a module reads in

The values in the compose files are for development. In production, set real secrets.