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:

  1. Set the real image registry + tag in k8s/overlays/prod/kustomization.yaml.

  2. Adjust the Ingress host (maidan.example.com placeholder) and TLS secret name.

  3. Apply the maidan-secrets Secret out-of-band using sealed-secrets, external-secrets, or a cloud-managed CSI secret provider.

  4. Apply the overlay:

    kubectl apply -k k8s/overlays/prod
    

Required secret keys

KeyRequired?Notes
DATABASE_URLyesPostgres connection string.
S3_ENDPOINTonly if S3 backendLands in Cluster E.
S3_BUCKETonly if S3 backend
S3_REGIONonly if S3 backend
S3_ACCESS_KEY_IDonly if S3 backend
S3_SECRET_ACCESS_KEYonly if S3 backend
OTLP_ENDPOINToptionalOTLP gRPC for traces; metrics when OTLP_METRICS=1.
OTLP_METRICSoptionalSet 1 to push /metrics instruments via OTLP.

base/secret.example.yaml documents the contract but contains no real values.

Image build matrix

ImageDockerfilePurpose
maidan-servercrates/maidan-server/DockerfileProduction binary.
maidan-servercrates/maidan-server/Dockerfile.devDev hot-reload.
maidan-postgresdocker/Dockerfile.dbPostgres + 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.