
Deploy Keyzori | (Just Updated) Self-Hosted Software License Key Server, Master Key Generated at Deploy
Keyzori license key server with REST API, Postgres, Redis and master key
keyzori
Just deployed
redis
Just deployed
Just deployed
/var/lib/postgresql
Deploy and Host Keyzori on Railway
Built for an LLM agent to deploy and use. The deploy form has no fields to fill: the root master key and both database passwords are generated at deploy time, PostgreSQL stores the licences on a volume, and every write answers 401 until the master key is sent. Keyzori is an open-source, self-hosted software licensing server: issue license keys with expiry dates, usage limits and device or IP caps, and check them from your app with one request.
About Hosting Keyzori
This template runs three services: keyzori (the licensing API on one public domain), postgres (PostgreSQL 18 with a volume mounted at /var/lib/postgresql) and redis (Redis 8, password protected, no persistence, as upstream runs it). Licences, users, items and API keys live in PostgreSQL, so they survive redeploys of any service.
Ready from boot. Keyzori refuses to start without a root key of the exact shape kz_key_ followed by 64 hexadecimal characters. This template generates it into the KZ_MASTER_KEY variable, and the database role the server uses is created on the first PostgreSQL boot, so a fresh deploy answers /health with PostgreSQL and Redis both up and needs no human step.
Using it. Create a licence with POST /licenses, enable it with POST /licenses/enable (new licences start disabled), then check it from your software with POST /validate. The create call returns the licence credential once; store it straight away. A rejected licence still answers HTTP 200, so read the code field (VALID, LICENSE_DISABLED, LICENSE_INVALID). Use the master key only to set up scoped API keys for everyday access.
Agent contract
- Public URL:
https://RAILWAY_PUBLIC_DOMAIN(the generated domain of thekeyzoriservice) - Private URL:
http://keyzori.railway.internal:8080 - Credentials: the variable
KZ_MASTER_KEYon thekeyzoriservice (there is no username) - Auth: send
Authorization: Bearer $KZ_MASTER_KEYon every management call, with a uniqueIdempotency-Keyheader on writes; a write without the key or with a wrong key answers401./validateneeds no key, it takes the licence credential in the body. - Health:
GET /healthreturns 200 with{"healthy":true,"postgresql":true,"redis":true}
#!/bin/sh
# needs: SITE_URL (public https URL of the keyzori service) and KZ_MASTER_KEY (its variable); also curl, grep, cut
H="Content-Type: application/json"
A="Authorization: Bearer $KZ_MASTER_KEY"
K="kz-check-$$-$(date +%s)"
curl -s -w "\nhealth: %{http_code}\n" "$SITE_URL/health"
curl -s -o /dev/null -w "write without key: %{http_code}\n" -X POST -H "$H" -H "Idempotency-Key: $K-a" -d '{}' "$SITE_URL/licenses"
curl -s -o /dev/null -w "write with wrong key: %{http_code}\n" -X POST -H "$H" -H "Idempotency-Key: $K-b" -H "Authorization: Bearer kz_key_0000000000000000000000000000000000000000000000000000000000000000" -d '{}' "$SITE_URL/licenses"
R=$(curl -s -X POST -H "$H" -H "$A" -H "Idempotency-Key: $K-c" -d '{}' "$SITE_URL/licenses")
ID=$(echo "$R" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
CRED=$(echo "$R" | grep -o '"credential":"[^"]*"' | cut -d'"' -f4)
echo "license: ${ID:+created}"
curl -s -o /dev/null -w "enable: %{http_code}\n" -X POST -H "$H" -H "$A" -H "Idempotency-Key: $K-d" -d "{\"ids\":[\"$ID\"]}" "$SITE_URL/licenses/enable"
echo "validate: $(curl -s -X POST -H "$H" -d "{\"license\":\"$CRED\"}" "$SITE_URL/validate" | grep -o '"code":"[A-Z_]*"')"
curl -s -o /dev/null -w "disable: %{http_code}\n" -X POST -H "$H" -H "$A" -H "Idempotency-Key: $K-e" -d "{\"ids\":[\"$ID\"]}" "$SITE_URL/licenses/disable"
echo "validate after disable: $(curl -s -X POST -H "$H" -d "{\"license\":\"$CRED\"}" "$SITE_URL/validate" | grep -o '"code":"[A-Z_]*"')"
A fresh deploy prints the health JSON, health: 200, write without key: 401, write with wrong key: 401, license: created, enable: 200, validate: "code":"VALID", disable: 200 and validate after disable: "code":"LICENSE_DISABLED".
Why Deploy Keyzori on Railway?
Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.
By deploying Keyzori on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.
- Agent-ready - no deployer fields, a contract block and one script that proves the refusal without a key, a create, an enable, a validate and a disable.
- Locked by default - the API answers 401 to every write until the generated master key is sent.
- Data survives redeploys - licences sit in PostgreSQL on a volume, and the server connects as a restricted database role rather than the administrator.
- Pinned - the upstream image and both database images are pinned by digest.
- No per-licence pricing - unlimited licences on your own domain.
Common Use Cases
- License keys for a desktop or CLI app - issue a key per customer and validate it at startup.
- Trials and expiring licences - set an expiry date and let validation refuse the key afterwards.
- Seat and device limits - cap devices or IP addresses per licence.
- Usage-metered features - count requests against a licence meter from your backend.
Dependencies for Keyzori Hosting
- The keyzori service, a PostgreSQL service with a volume and a Redis service, all created by the template. A custom domain is optional.
Deployment Dependencies
Template Content
