PostgreSQL first start
This page is for the self-hosted PostgreSQL profile. The AWS and CockroachDB Cloud deployment has its own deployment page. This guide takes a host that already has Docker and an NVIDIA runtime all the way to a deployment that answers a real find. It helps to know which services exist from Deployment topology, and that the machine is big enough per Hardware and cost.
The run is two tracks that meet at the health check. The default profile gives you a private engine you can already probe. The Logto and public track adds the internet-facing surface on top.
fill .env ─▶ up -d default ─┬──────────────────────────▶ admin health │ ▲ └▶ Logto apps ─▶ auth apply ─▶ up -d publicCompose reads one file, the project .env at the package root, and every command names it
explicitly. Follow the steps in order.
-
Fill the environment. Start from the committed example and generate a fresh secret for each blank.
Terminal window cp src/deploy/.env.example .envopenssl rand -base64 32Six values are hard requirements. Compose interpolates them with
:?, so it refuses to start at all while any of them is blank.Terminal window AIZK_ADMIN_PASSWORD=AIZK_APP_PASSWORD=AIZK_LOGTO_DB_PASSWORD=AIZK_OBJECT_STORE_ACCESS_KEY=AIZK_OBJECT_STORE_SECRET_KEY=AIZK_DOCLING_API_KEY= -
Start the local stack, the default profile, which is the engine with no public surface.
Terminal window docker compose --env-file .env -f src/deploy/docker-compose.yml up -dThe first start is slow, because the three vLLM lanes come up one after another and each one downloads and loads weights.
setupruns the migrations once and exits. Nothing is exposed to the network yet. -
Create the four Logto applications. The public profile needs Logto to already know about aizk, so make these in the Logto console and paste their credentials into
.env.Application Kind Setting pair Management API client machine to machine, role carrying the Management API allpermissionAIZK_LOGTO_CLIENT_ID,AIZK_LOGTO_CLIENT_SECRETMCP clients native public client, redirects are the fixed loopback callbacks used by supported clients distribute its public client ID in client setup Browser app traditional web, redirect is exactly ${AIZK_WEB_PUBLIC_URL}/auth/sign-in-callbackAIZK_WEB_CLIENT_ID,AIZK_WEB_CLIENT_SECRETAIZK Admin traditional web, redirect is exactly ${AIZK_ADMIN_PUBLIC_URL}/oauth2/callbackAIZK_ADMIN_CLIENT_ID,AIZK_ADMIN_CLIENT_SECRETSet the AIZK Admin application’s Mandatory MFA to passkey. That setting is per application, so it asks operators for a second factor without changing how anyone signs in to the memory itself. Add
AIZK_ADMIN_COOKIE_SECRETas its own fresh 32 bytes, since oauth2-proxy signs the console session cookie with it.Then set the URLs.
AIZK_LOGTO_URLis where Logto answers, andAIZK_MCP_PUBLIC_URL,AIZK_WEB_PUBLIC_URLandAIZK_API_PUBLIC_URLare the one public origin the tunnel serves. All of them must be HTTPS. AddAIZK_WEB_SESSION_SECRETof at least 32 bytes, generated separately, sinceSettings.independent_session_secretrejects it when it matches the web, Management API or OAuth client secret. Finally addAIZK_TUNNEL_TOKENfrom Cloudflare. Caddy keeps the browser and the API same-origin by routing, so neither needs its own published port. -
Reconcile the authorization policy. aizk owns a small committed slice of the Logto policy.
Terminal window aizk admin auth auditaizk admin auth applyauditreports drift and exits nonzero when the live tenant does not match.applyreconciles it and leaves unrelated roles and permissions alone. Both are idempotent, and The Logto boundary explains what aizk does and does not own here.applycreates two global roles,aizk-userfor everyone andaizk-adminfor operators. Assignaizk-adminto yourself, then confirm it withaizk admin auth roles, because that role is the only thing the operator console admits. -
Move the Logto console to its own hostname, on its own, before anything else changes.
Logto’s console must own an origin, so it cannot live under a path on the operator console host. Add a Cloudflare public hostname such as
console.example.compointing atlogto:3002, setAIZK_LOGTO_ADMIN_ENDPOINT=https://console.example.com, and recreate thelogtoservice. Logto bakes that endpoint into the console’s own redirect URIs, so a half-finished move locks the console out and the only fast repair is putting the old line back. -
Add the two remaining Cloudflare hostnames,
aizk.example.comtoweb:8081andadmin.example.comtoweb:8082. Every public name is a first-level subdomain, which is what keeps them inside the free Universal SSL certificate. Deployment topology has the full routing table and explains the optional Cloudflare Access layer in front of the console. -
Start the public profile, which adds everything internet-facing.
Terminal window docker compose --profile public --env-file .env -f src/deploy/docker-compose.yml up -dThe ordering fails closed.
cloudflaredmust report ready beforelogto-setupruns, because the tunnel is what publishes Logto’s canonical issuer.public-checkthen runsadmin auth check-publicwithAIZK_REQUIRE_AUTH=1, which buildsSettingsand so fails when the Logto URL, the public URLs or either OAuth client is missing or half filled.web-checkdoes the same for the browser settings, and the MCP server waits until both gates pass. -
Check it works with the health command, run inside the private worker.
Terminal window docker compose --env-file .env -f src/deploy/docker-compose.yml exec -T worker aizk admin health
What a healthy deployment reports
Section titled “What a healthy deployment reports”Run the health command in the private worker, never in server. The public process has no
migration-owner credential on purpose, so a compromised request path cannot turn this diagnostic
into an RLS bypass.
A healthy deployment reports an up-to-date migration, an empty rls_violations list, Logto
identity mode, all four model endpoints reachable with matching served aliases, no retained queue
failures, and a find block with candidates and no error. That last field is the one that
matters, because it is a real retrieval through the real models rather than a ping.
- PostgreSQL profile covers tuning before your data grows.
- Backups and recovery should be set up on day one.
- Observability adds the logging profile and the queue doctor.
- The release gate is the checklist before real traffic.