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:
| tag | tracks |
|---|---|
0.1.0 | this patch — pin this |
0.1 | current 0.1.x |
0 | current 0.x |
latest | current 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.