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

Deploy Keyzori | (Just Updated) Self-Hosted Software License Key Server, Master Key Generated at Deploy

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 the keyzori service)
  • Private URL: http://keyzori.railway.internal:8080
  • Credentials: the variable KZ_MASTER_KEY on the keyzori service (there is no username)
  • Auth: send Authorization: Bearer $KZ_MASTER_KEY on every management call, with a unique Idempotency-Key header on writes; a write without the key or with a wrong key answers 401. /validate needs no key, it takes the licence credential in the body.
  • Health: GET /health returns 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

More templates in this category

View Template
Rocky Linux
Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀

codestorm
51
View Template
Foundry Virtual Tabletop
A Self-Hosted & Modern Roleplaying Platform

Lucas
71
View Template
Letta Code Remote
Run a Letta Code agent 24/7. No inbound ports, just deploy.

Letta
52