Deploy
Deploy
Your MinMaxKey server, running. One container, two minutes, no orchestration.
Image: pinned on GitHub Container Registry —
ghcr.io/maxing-tech/minmaxkey:1.0.0-rc. While the repo is private the image is private too (docker login ghcr.ioonce with a GitHub account); from the public release onward it pulls with no login.
Docker
MMK_ADMIN_TOKEN=$(openssl rand -base64 32) # management API key
MMK_ADMIN_PASSWORD=$(openssl rand -base64 16) # dashboard login
MMK_SECRET_KEY=$(openssl rand -base64 32) # session cookie signing
docker run -d -p 8080:8080 \
-v ./data:/data \
-e MMK_ADMIN_TOKEN="$MMK_ADMIN_TOKEN" \
-e MMK_ADMIN_PASSWORD="$MMK_ADMIN_PASSWORD" \
-e MMK_SECRET_KEY="$MMK_SECRET_KEY" \
ghcr.io/maxing-tech/minmaxkey:1.0.0-rc
curl -s localhost:8080/healthz
# {"status": "ok", "service": "minmaxkey"}
That's it. The dashboard is at http://localhost:8080/admin (login with
MMK_ADMIN_PASSWORD), the API explorer at /docs.
Keep the three secrets somewhere safe — you'll need MMK_ADMIN_TOKEN for the
CLI and webhook automation.
Docker Compose
# docker-compose.yml
services:
minmaxkey:
image: ghcr.io/maxing-tech/minmaxkey:1.0.0-rc
ports: ["8080:8080"]
environment:
MMK_ADMIN_TOKEN: ${MMK_ADMIN_TOKEN:?set in your shell}
MMK_ADMIN_PASSWORD: ${MMK_ADMIN_PASSWORD:-admin}
MMK_SECRET_KEY: ${MMK_SECRET_KEY:?set in your shell}
volumes:
- ./data:/data
restart: unless-stopped
export MMK_ADMIN_TOKEN=$(openssl rand -base64 32)
export MMK_SECRET_KEY=$(openssl rand -base64 32)
docker compose up -d
Bare Python (no Docker)
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
export MMK_ADMIN_TOKEN=$(openssl rand -base64 32)
export MMK_ADMIN_PASSWORD=$(openssl rand -base64 16)
export MMK_SECRET_KEY=$(openssl rand -base64 32)
uvicorn app.main:app --host 0.0.0.0 --port 8080
Environment variables (the only config there is)
| variable | default | notes |
|---|---|---|
MMK_DATABASE_PATH |
data/mmk.db |
SQLite file location |
MMK_DATABASE_URL |
— | set to use Postgres instead |
MMK_ADMIN_TOKEN |
dev-admin-token |
management API auth — change it |
MMK_ADMIN_PASSWORD |
admin |
dashboard login — change it |
MMK_SECRET_KEY |
change-me |
cookie signing — change it, keep stable |
MMK_COOKIE_SECURE |
false |
true when serving over HTTPS |
MMK_ALLOWED_ORIGINS |
* |
comma-separated CORS allowlist |
MMK_RESEND_API_KEY |
— | Resend API key — key emails, 2-min setup |
MMK_POSTMARK_TOKEN |
— | Postmark server token — key emails |
MMK_EMAIL_FROM |
— | sender address for any backend (verified sender) |
MMK_SMTP_HOST |
— | classic SMTP server — key emails |
MMK_SMTP_PORT |
587 |
SMTP port (STARTTLS) |
MMK_SMTP_USER / MMK_SMTP_PASS |
— | SMTP login (empty = no auth) |
MMK_SMTP_FROM |
MinMaxKey <no-reply@…> |
sender address (also works for the API backends) |
MMK_SMTP_TLS |
true |
STARTTLS on connect (port 587) |
MMK_SMTP_SSL |
false |
implicit TLS (port 465); takes precedence over MMK_SMTP_TLS |
Setting any email backend turns on key delivery by email: the buyer gets their license key on checkout and a note on each renewal. Resend is the two-minute path — Email delivery.
Generate secrets: python -c "import secrets; print(secrets.token_urlsafe(32))".
Backups
/data holds the whole database. Copy the file, or point Litestream / restic
/ borg at the directory. That's the entire backup story.
Done? Head to Use it to issue your first license — or skip ahead to deeper ops (Postgres, reverse proxy, upgrades): self-hosting.