efelant efelant

Self-host

Run Efelant on hardware or a VM you control.

Core is PostgreSQL. The Flutter web client and WebSocket gateway are optional adapters for browsers. There is no application server to operate. Do not invent custom PostgreSQL HA — use StackGres, CloudNativePG, Patroni, or a managed service.

Overview

What you run

postgres

SQL, REST :18080, gRPC-JSON :18081. Always.

web-gateway

Browser WebSocket adapter. Skip if clients use the Postgres wire.

web

Standalone Flutter messenger UI. Skip if you only embed.

site

This documentation site. Optional.

Images live at ghcr.io/tafaust/efelant/<name>. Tags: 0, 0.1, 0.1.0, latest. Commands below pin 0.1.0. See GHCR.

Native clients talk PostgreSQL directly (efelant_app, function EXECUTE only). REST and gRPC-JSON are background workers inside Postgres.

Containers

Docker

Single machine, no Compose file. Generate passwords with ./scripts/new-secrets.sh. The image entrypoint mints a self-signed cert; pass the same -c flags Compose uses so REST/gRPC load. Init SQL runs on the first empty volume. Later: ./scripts/migrate.sh. Do not publish 5432 on a public NIC.

docker network create efelant
docker volume create efelant_pgdata

docker run -d --name efelant-postgres --network efelant \
  --restart unless-stopped \
  -v efelant_pgdata:/var/lib/postgresql/data \
  -e POSTGRES_USER=postgres \
  -e POSTGRES_PASSWORD="$POSTGRES_PASSWORD" \
  -e POSTGRES_DB=efelant \
  -e EFELANT_APP_PASSWORD="$EFELANT_APP_PASSWORD" \
  -e EFELANT_MIGRATOR_PASSWORD="$EFELANT_MIGRATOR_PASSWORD" \
  -e EFELANT_SEED=0 \
  -p 5432:5432 -p 18080:18080 -p 18081:18081 \
  ghcr.io/tafaust/efelant/postgres:0.1.0 \
  postgres \
  -c listen_addresses=* \
  -c ssl=on \
  -c ssl_cert_file=/var/lib/postgresql/certs/server.crt \
  -c ssl_key_file=/var/lib/postgresql/certs/server.key \
  -c password_encryption=scram-sha-256 \
  -c shared_preload_libraries=pg_cron,efelant_rest,efelant_grpc \
  -c cron.database_name=efelant

docker run -d --name efelant-web-gateway --network efelant \
  --restart unless-stopped \
  -e EFELANT_DB_HOST=efelant-postgres \
  -e EFELANT_DB_PORT=5432 \
  -e EFELANT_DB_NAME=efelant \
  -e EFELANT_DB_USER=efelant_app \
  -e EFELANT_DB_PASSWORD="$EFELANT_APP_PASSWORD" \
  -e EFELANT_DB_SSLMODE=disable \
  -e EFELANT_WS_PORT=5433 \
  -e EFELANT_WS_ORIGINS=https://chat.example.com \
  -p 5433:5433 \
  ghcr.io/tafaust/efelant/web-gateway:0.1.0

docker run -d --name efelant-web --network efelant \
  --restart unless-stopped \
  -p 8080:80 \
  ghcr.io/tafaust/efelant/web:0.1.0

Docker Compose

Fastest path on a VM. Local trial from the repository root:

cp .env.example .env          # local trial only
./scripts/up.sh

Open http://localhost:8080. Postgres, gateway, and Flutter web come up together.

Your VM with TLS (Postgres unpublished). Set EFELANT_SEED=0 so development users are not created.

./scripts/new-secrets.sh
# edit .env: EFELANT_DOMAIN, EFELANT_WS_ORIGINS
docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d --build

Caddy on 80/443 for EFELANT_DOMAIN. Traefik instead:

docker compose -f docker-compose.yml -f docker-compose.traefik.yml up -d --build

Docker Swarm

Same three services as a stack. Swarm does not build images — pull GHCR, tag to the names Compose uses, then deploy. depends_on conditions and container_name are ignored. Do not run a homemade Postgres primary/replica on Swarm.

docker pull ghcr.io/tafaust/efelant/postgres:0.1.0
docker pull ghcr.io/tafaust/efelant/web-gateway:0.1.0
docker pull ghcr.io/tafaust/efelant/web:0.1.0
docker tag ghcr.io/tafaust/efelant/postgres:0.1.0 efelant-postgres:0.1.0
docker tag ghcr.io/tafaust/efelant/web-gateway:0.1.0 efelant-web-gateway:0.1.0
docker tag ghcr.io/tafaust/efelant/web:0.1.0 efelant-web:0.1.0
docker stack deploy -c docker-compose.yml efelant

The Caddy/Traefik overlays use Compose !reset for ports; that merge is for Compose, not docker stack deploy. For TLS on Swarm, add a published proxy yourself or run Compose on a single node. Prefer a dedicated Postgres (or managed) and keep the stack as gateway + web.

Kubernetes

Helm

Chart deploy/helm/efelant (also OCI on GHCR). A Job applies SQL. Optional gateway Deployment (gateway.enabled defaults to false). It does not install PostgreSQL.

kubectl create namespace efelant
kubectl create secret generic efelant-postgres \
  --namespace efelant \
  --from-literal=password="$POSTGRES_PASSWORD"

helm install efelant oci://ghcr.io/tafaust/efelant/charts/efelant --version 0.1.0 \
  --namespace efelant \
  --set postgres.host=efelant-postgres \
  --set postgres.existingSecret=efelant-postgres \
  --set gateway.enabled=true

From a checkout: helm install efelant ./deploy/helm/efelant -n efelant. Point postgres.host at StackGres, CloudNativePG, or any Postgres with pgcrypto, citext, and pg_trgm.

Kustomize

Manifests in deploy/kustomize. Base is namespace, config, secret, migration Job, gateway, and web. Edit secrets before apply — the checked-in secret is a placeholder.

kubectl apply -k deploy/kustomize/overlays/dev
# single Postgres Deployment + gateway + web

kubectl apply -k deploy/kustomize/overlays/prod
# StackGres component + Traefik, 2 replicas

kubectl apply -k deploy/kustomize/overlays/prod-no-stackgres
# Traefik + 2 replicas, you bring Postgres

components/stackgres is a Kustomize component, not an overlay. Dev does not include it.

Postgres

Extensions and roles

Required: pgcrypto, citext, pg_trgm. Optional: pg_cron. REST/gRPC need shared_preload_libraries=pg_cron,efelant_rest,efelant_grpc (already in the Efelant Postgres image). See REST / gRPC.

Do not put PostgREST, FastAPI, or a gRPC microservice in front of Core.

efelant_owner

Owns objects, DEFINER.

efelant_migrator

Applies SQL.

efelant_app

Client login, function EXECUTE only.

Distribution

GHCR

Push to main or a v* tag publishes images and the Helm chart. Every image gets these tags:

tagtracks
0.1.0this patch — pin this
0.1current 0.1.x
0current 0.x
latestcurrent main
docker pull ghcr.io/tafaust/efelant/postgres:0.1.0
docker pull ghcr.io/tafaust/efelant/postgres:0.1
docker pull ghcr.io/tafaust/efelant/postgres:0
docker pull ghcr.io/tafaust/efelant/postgres:latest

docker pull ghcr.io/tafaust/efelant/web-gateway:0.1.0   # also :0.1 :0 :latest
docker pull ghcr.io/tafaust/efelant/web:0.1.0
docker pull ghcr.io/tafaust/efelant/site:0.1.0

Helm chart is the patch only: --version 0.1.0. First packages may be private. Make them public under GitHub → Packages if the repo is public.

Docs site only

GitHub Pages: efelant.de (site/CNAME). Settings → Pages → GitHub Actions. Rootless nginx: docker compose -f docker-compose.site.yml up --build then http://localhost:8090.