--- title: "Hosting member-console" audience: [admin] summary: "Build the container image and generate the secrets needed to run a member-console instance." --- # Hosting member-console Operational steps for running your own member-console instance. For how the console fits alongside a public site and identity provider, see [deployment-architecture.md](deployment-architecture.md); for identity-provider configuration, see [identity-provider-setup.md](identity-provider-setup.md). ## Building the image The container image is built with Docker Buildx for both ARM64 and AMD64. Tag it for your own registry: ```bash docker buildx build \ --platform linux/arm64,linux/amd64 \ -t your-registry.example/your-org/member-console:latest \ -t your-registry.example/your-org/member-console:$(date +%Y-%m-%d) \ --push \ . ``` > The canonical image is published to `git.coopcloud.tech/wiki-cafe/member-console`. > Substitute your own registry when self-hosting. ## Secrets Three configuration keys are secrets, each with a `-file` variant that reads the value from a file at startup (`docs/environment-reference.md`): - `oidc-sp-client-secret` — the OIDC client's secret, issued by the identity provider. - `valkey-password` — the session store's password. Required; the console refuses to start without one. - `temporal-oauth-client-secret` — only when Temporal is behind OAuth. None of them is generated by or for the console: each is a credential for something external, obtained from that system. Sessions are stored server-side in Valkey and carry no signing secret of their own; rotating any of the three means updating the deployment's configuration and restarting. ## Session store limits Every key member-console writes to the session store carries a TTL: a signed-in session lives for the deployment's configured lifetime, and a pre-sign-in session, the one `/login` creates to hold the OIDC state, nonce, and PKCE verifier, lives fifteen minutes. A bare, unauthenticated request no longer writes anything to the store. A flood of `/login` requests still writes one short-lived key per request, so the store's memory is still a resource a flood can spend. Set `maxmemory` on the Valkey instance to a bound sized for the deployment's expected number of concurrent sessions, and set `maxmemory-policy` to `volatile-lru`. Under that policy, once the bound is reached, Valkey evicts the least recently used session first; that session can belong to a signed-in person, and losing it signs them out, so they sign in again. `noeviction`, Valkey's default, is worse: once `maxmemory` is reached it rejects new writes instead, and a write rejected during `/login` or `/callback` turns the flood into failed sign-ins for everyone hitting the store, not just whichever session would have been evicted. Rate limit `/login` at the reverse proxy, since the memory bound above caps damage but does not stop a flood from reaching the store in the first place. With Caddy and the caddy-ratelimit plugin's `rate_limit` directive: ```caddyfile handle /login* { rate_limit { zone login { key {remote_host} events 20 window 1m } } reverse_proxy member-console:8080 } ``` Any reverse proxy that can key a limit by client address and scope it to one path serves the same purpose.