Deploy
How to run Maidan locally and in a Kubernetes cluster. Refer to Architecture.md for what each component does.
Local: Docker Compose
Prod-style stack
Builds the production image from crates/maidan-server/Dockerfile and a
custom Postgres image (pgvector base; schema is applied at runtime by
maidan-server, not baked into the image).
docker compose up # postgres only
docker compose --profile full up # + minio + maidan-server
curl http://localhost:8080/health # after maidan-server lands /health
Hot-reload dev stack
docker compose -f compose.dev.yaml up
maidan-server runs under cargo watch with the workspace mounted as a
volume, so source edits trigger an in-container rebuild.
To leave the server outside the container and only run the deps:
docker compose -f compose.dev.yaml up postgres minio
DATABASE_URL=postgres://maidan:maidan@localhost:5432/maidan cargo run --bin maidan-server
Without Docker (SQLite)
For pure host development against SQLite — no docker compose needed:
DATABASE_URL=sqlite://./dev.db cargo run --bin maidan-server
Or against an in-memory SQLite (lost on shutdown):
DATABASE_URL=sqlite::memory: cargo run --bin maidan-server
The server detects the dialect from the DATABASE_URL prefix
(postgres://, postgresql://, or sqlite:) and selects the
appropriate migration runner and backend automatically.
Kubernetes
Manifests are under k8s/ and use Kustomize.
k8s/
├── base/ # canonical resources
└── overlays/
├── dev/ # local kind/minikube
└── prod/ # production cluster
Dev cluster (kind)
kind create cluster --name maidan
# build images and load them into the cluster
docker build -t maidan-server:dev -f crates/maidan-server/Dockerfile .
docker build -t maidan-postgres:dev -f docker/Dockerfile.db .
kind load docker-image maidan-server:dev --name maidan
kind load docker-image maidan-postgres:dev --name maidan
kubectl apply -k k8s/overlays/dev
kubectl -n maidan rollout status deploy/maidan-server
kubectl -n maidan port-forward svc/maidan-server 8080:8080
curl http://localhost:8080/health
Production cluster
The prod overlay is a template. Before applying:
-
Set the real image registry + tag in
k8s/overlays/prod/kustomization.yaml. -
Adjust the Ingress host (
maidan.example.complaceholder) and TLS secret name. -
Apply the
maidan-secretsSecret out-of-band using sealed-secrets, external-secrets, or a cloud-managed CSI secret provider. -
Apply the overlay:
kubectl apply -k k8s/overlays/prod
Required secret keys
| Key | Required? | Notes |
|---|---|---|
DATABASE_URL | yes | Postgres connection string. |
S3_ENDPOINT | only if S3 backend | Lands in Cluster E. |
S3_BUCKET | only if S3 backend | |
S3_REGION | only if S3 backend | |
S3_ACCESS_KEY_ID | only if S3 backend | |
S3_SECRET_ACCESS_KEY | only if S3 backend | |
OTLP_ENDPOINT | optional | OTLP gRPC for traces; metrics when OTLP_METRICS=1. |
OTLP_METRICS | optional | Set 1 to push /metrics instruments via OTLP. |
base/secret.example.yaml documents the contract but contains no real
values.
Image build matrix
| Image | Dockerfile | Purpose |
|---|---|---|
maidan-server | crates/maidan-server/Dockerfile | Production binary. |
maidan-server | crates/maidan-server/Dockerfile.dev | Dev hot-reload. |
maidan-postgres | docker/Dockerfile.db | Postgres + pgvector. |
Migrations
maidan-server is the single source of truth for schema; it applies
all pending migrations on boot via run_postgres_migrations (or
run_sqlite_migrations, depending on the dialect). The
maidan-postgres image is a thin pgvector layer that does not
bundle schema into docker-entrypoint-initdb.d — fresh volumes and
upgrades go through the same code path.